From 01d4b2246438996729bdc48523fd2991847d4e13 Mon Sep 17 00:00:00 2001 From: Matei Date: Sun, 16 Sep 2018 10:59:38 -0700 Subject: [PATCH] Tanks v0.4.0 - Added the level creator, and saved levels! --- src/tanks/Bullet.java | 95 +- src/tanks/{Flame.java => BulletFlame.java} | 6 +- src/tanks/BulletLaser.java | 39 + src/tanks/Button.java | 81 +- src/tanks/Effect.java | 198 ++- src/tanks/EnemyTank.java | 250 ++-- src/tanks/Firework.java | 6 +- src/tanks/Game.java | 106 +- ...yInputListener.java => InputKeyboard.java} | 3 +- ...ouseInputListener.java => InputMouse.java} | 2 +- ...ollInputListener.java => InputScroll.java} | 2 +- src/tanks/LaserBullet.java | 39 - src/tanks/Level.java | 190 ++- src/tanks/LevelGenerator.java | 13 +- src/tanks/Mine.java | 95 +- src/tanks/Movable.java | 20 +- src/tanks/Panel.java | 115 +- src/tanks/Ray.java | 23 +- .../{Registry.java => RegistryTank.java} | 15 +- src/tanks/Screen.java | 4 +- src/tanks/ScreenAdjustWindow.java | 20 + src/tanks/ScreenCrashed.java | 36 +- src/tanks/ScreenGame.java | 263 +++- src/tanks/ScreenInterlevel.java | 54 +- src/tanks/ScreenLevelBuilder.java | 1181 +++++++++++++++-- src/tanks/ScreenOptions.java | 48 +- src/tanks/ScreenPaused.java | 80 -- src/tanks/ScreenSavedLevels.java | 164 +-- src/tanks/ScreenTitle.java | 29 +- src/tanks/SoundThread.java | 70 + src/tanks/Tank.java | 101 +- .../{EnemyTankBlack.java => TankBlack.java} | 8 +- .../{EnemyTankBrown.java => TankBrown.java} | 6 +- ...yTankDarkGreen.java => TankDarkGreen.java} | 8 +- .../{EnemyTankGray.java => TankGray.java} | 8 +- .../{EnemyTankGreen.java => TankGreen.java} | 6 +- ...EnemyTankMagenta.java => TankMagenta.java} | 6 +- .../{EnemyTankMini.java => TankMini.java} | 26 +- .../{EnemyTankMint.java => TankMint.java} | 10 +- .../{EnemyTankOrange.java => TankOrange.java} | 12 +- .../{EnemyTankPink.java => TankPink.java} | 10 +- .../{PlayerTank.java => TankPlayer.java} | 69 +- .../{EnemyTankPurple.java => TankPurple.java} | 4 +- src/tanks/{EnemyTankRed.java => TankRed.java} | 41 +- ...EnemyTankUnknown.java => TankUnknown.java} | 7 +- .../{EnemyTankWhite.java => TankWhite.java} | 26 +- .../{EnemyTankYellow.java => TankYellow.java} | 4 +- src/tanks/Team.java | 52 + src/tanks/TextBox.java | 191 +++ src/tanks/Turret.java | 11 +- src/tanks/Window.java | 304 ++++- src/tanks/legacy/EnemyTankBlack.java | 2 +- src/tanks/legacy/EnemyTankBrown.java | 2 +- src/tanks/legacy/EnemyTankDarkRed.java | 2 +- src/tanks/legacy/EnemyTankGray.java | 2 +- src/tanks/legacy/EnemyTankGreen.java | 2 +- src/tanks/legacy/EnemyTankMini.java | 2 +- src/tanks/legacy/EnemyTankMint.java | 2 +- src/tanks/legacy/EnemyTankOrange.java | 2 +- src/tanks/legacy/EnemyTankPink.java | 2 +- src/tanks/legacy/EnemyTankPinkMagenta.java | 2 +- src/tanks/legacy/EnemyTankPurple.java | 136 +- src/tanks/legacy/EnemyTankPurple2.java | 2 +- src/tanks/legacy/EnemyTankRed.java | 4 +- src/tanks/legacy/EnemyTankWhite.java | 2 +- src/tanks/legacy/EnemyTankYellow.java | 2 +- src/tanks/resources/bounce.wav | Bin 0 -> 2366 bytes src/tanks/resources/bullet_explode.wav | Bin 0 -> 13890 bytes src/tanks/resources/destroy.wav | Bin 0 -> 39382 bytes src/tanks/resources/explosion.wav | Bin 0 -> 61684 bytes src/tanks/{ => resources}/icon.png | Bin src/tanks/{ => resources}/icon64.png | Bin src/tanks/{ => resources}/loading.png | Bin src/tanks/resources/lose.wav | Bin 0 -> 15920 bytes src/tanks/resources/shoot.wav | Bin 0 -> 9274 bytes src/tanks/resources/win.wav | Bin 0 -> 27804 bytes 76 files changed, 3232 insertions(+), 1091 deletions(-) rename src/tanks/{Flame.java => BulletFlame.java} (85%) mode change 100755 => 100644 create mode 100644 src/tanks/BulletLaser.java rename src/tanks/{KeyInputListener.java => InputKeyboard.java} (83%) mode change 100755 => 100644 rename src/tanks/{MouseInputListener.java => InputMouse.java} (96%) mode change 100755 => 100644 rename src/tanks/{ScrollInputListener.java => InputScroll.java} (82%) mode change 100755 => 100644 delete mode 100755 src/tanks/LaserBullet.java rename src/tanks/{Registry.java => RegistryTank.java} (94%) mode change 100755 => 100644 create mode 100644 src/tanks/ScreenAdjustWindow.java delete mode 100755 src/tanks/ScreenPaused.java create mode 100644 src/tanks/SoundThread.java rename src/tanks/{EnemyTankBlack.java => TankBlack.java} (75%) mode change 100755 => 100644 rename src/tanks/{EnemyTankBrown.java => TankBrown.java} (74%) mode change 100755 => 100644 rename src/tanks/{EnemyTankDarkGreen.java => TankDarkGreen.java} (75%) mode change 100755 => 100644 rename src/tanks/{EnemyTankGray.java => TankGray.java} (71%) mode change 100755 => 100644 rename src/tanks/{EnemyTankGreen.java => TankGreen.java} (77%) mode change 100755 => 100644 rename src/tanks/{EnemyTankMagenta.java => TankMagenta.java} (65%) mode change 100755 => 100644 rename src/tanks/{EnemyTankMini.java => TankMini.java} (73%) mode change 100755 => 100644 rename src/tanks/{EnemyTankMint.java => TankMint.java} (61%) mode change 100755 => 100644 rename src/tanks/{EnemyTankOrange.java => TankOrange.java} (68%) mode change 100755 => 100644 rename src/tanks/{EnemyTankPink.java => TankPink.java} (70%) mode change 100755 => 100644 rename src/tanks/{PlayerTank.java => TankPlayer.java} (60%) mode change 100755 => 100644 rename src/tanks/{EnemyTankPurple.java => TankPurple.java} (68%) mode change 100755 => 100644 rename src/tanks/{EnemyTankRed.java => TankRed.java} (72%) mode change 100755 => 100644 rename src/tanks/{EnemyTankUnknown.java => TankUnknown.java} (77%) mode change 100755 => 100644 rename src/tanks/{EnemyTankWhite.java => TankWhite.java} (57%) mode change 100755 => 100644 rename src/tanks/{EnemyTankYellow.java => TankYellow.java} (73%) mode change 100755 => 100644 create mode 100644 src/tanks/Team.java create mode 100644 src/tanks/TextBox.java create mode 100644 src/tanks/resources/bounce.wav create mode 100644 src/tanks/resources/bullet_explode.wav create mode 100644 src/tanks/resources/destroy.wav create mode 100644 src/tanks/resources/explosion.wav rename src/tanks/{ => resources}/icon.png (100%) mode change 100755 => 100644 rename src/tanks/{ => resources}/icon64.png (100%) mode change 100755 => 100644 rename src/tanks/{ => resources}/loading.png (100%) mode change 100755 => 100644 create mode 100644 src/tanks/resources/lose.wav create mode 100644 src/tanks/resources/shoot.wav create mode 100644 src/tanks/resources/win.wav diff --git a/src/tanks/Bullet.java b/src/tanks/Bullet.java index 1ae16457..62122aad 100755 --- a/src/tanks/Bullet.java +++ b/src/tanks/Bullet.java @@ -6,14 +6,15 @@ public class Bullet extends Movable { - public static enum BulletEffect {none, fire, fireTrail, trail}; + public static enum BulletEffect {none, fire, darkFire, fireTrail, trail}; public static int bullet_size = 10; public int age = 0; public double size; public int bounces; - public Color color; + public Color baseColor; + public Color outlineColor; public double destroyTimer = 0; public Tank tank; public double damage = 1; @@ -21,15 +22,17 @@ public static enum BulletEffect {none, fire, fireTrail, trail}; public boolean useCustomWallCollision = false; public double wallCollisionSize = 10; - public Bullet(double x, double y, Color color, int bounces, Tank t) + public Bullet(double x, double y, int bounces, Tank t) { super(x, y); this.vX = 0; this.vY = 0; this.size = bullet_size; - this.color = color; + this.baseColor = t.color; + this.outlineColor = Team.getObjectColor(t.turret.color, t); this.bounces = bounces; this.tank = t; + this.team = t.team; t.liveBullets++; } @@ -50,7 +53,7 @@ public void checkCollision() { double prevX = this.posX; double prevY = this.posY; - + Obstacle o = Game.obstacles.get(i); double horizontalDist = Math.abs(this.posX - o.posX); @@ -62,7 +65,7 @@ public void checkCollision() double s = this.size; if (useCustomWallCollision) s = this.wallCollisionSize; - + double bound = s / 2 + Obstacle.obstacle_size / 2; if (horizontalDist < bound && verticalDist < bound) @@ -138,29 +141,33 @@ else if (dy >= 0 && dy < bound && horizontalDist < verticalDist) double verticalDist = Math.abs(this.posY - o.posY); Tank t = ((Tank) o); - + double bound = this.size / 2 + t.size / 2; if (horizontalDist < bound && verticalDist < bound) { - t.flashAnimation = 1; this.destroy = true; - this.vX = 0; - this.vY = 0; - t.lives -= this.damage; - if (t.lives <= 0) + if (!(Team.isAllied(this, t) && !this.team.friendlyFire)) { - t.flashAnimation = 0; - o.destroy = true; - if (o.equals(Game.player)) - Game.coins -= 5; - if (this.tank.equals(Game.player)) - Game.coins += t.coinValue; + t.flashAnimation = 1; + this.vX = 0; + this.vY = 0; + t.lives -= this.damage; + + if (t.lives <= 0) + { + t.flashAnimation = 0; + o.destroy = true; + if (o.equals(Game.player)) + Game.coins -= 5; + if (this.tank.equals(Game.player)) + Game.coins += t.coinValue; + } } } } - else if ((o instanceof Bullet || o instanceof Mine) && o != this && !o.destroy && !(o instanceof Flame || this instanceof Flame)) + else if ((o instanceof Bullet || o instanceof Mine) && o != this && !o.destroy && !(o instanceof BulletFlame || this instanceof BulletFlame)) { if (!o.destroy) { @@ -195,12 +202,18 @@ else if ((o instanceof Bullet || o instanceof Mine) && o != this && !o.destroy & if (collided) { + if (this.bounces <= 0) { + Window.playSound("resources/bullet_explode.wav"); + this.destroy = true; this.vX = 0; this.vY = 0; } + else + Window.playSound("resources/bounce.wav"); + this.bounces--; } } @@ -217,36 +230,54 @@ public void update() { if (destroy) { - if (this.destroyTimer <= 0 && Game.graphicalEffects && !(this instanceof Flame)) + if (this.destroyTimer <= 0 && Game.graphicalEffects && !(this instanceof BulletFlame)) { for (int i = 0; i < this.size * 4; i++) { - Effect e = new Effect(this.posX, this.posY, Effect.EffectType.piece); + Effect e = Effect.createNewEffect(this.posX, this.posY, Effect.EffectType.piece); int var = 50; e.maxAge /= 2; - e.col = new Color((int) Math.min(255, Math.max(0, this.color.getRed() + Math.random() * var - var / 2)), (int) Math.min(255, Math.max(0, this.color.getGreen() + Math.random() * var - var / 2)), (int) Math.min(255, Math.max(0, this.color.getBlue() + Math.random() * var - var / 2))); + e.col = new Color((int) Math.min(255, Math.max(0, this.baseColor.getRed() + Math.random() * var - var / 2)), (int) Math.min(255, Math.max(0, this.baseColor.getGreen() + Math.random() * var - var / 2)), (int) Math.min(255, Math.max(0, this.baseColor.getBlue() + Math.random() * var - var / 2))); e.setPolarMotion(Math.random() * 2 * Math.PI, Math.random() * this.size / 50.0 * 4); Game.effects.add(e); } } - + this.destroyTimer += Panel.frameFrequency; this.vX = 0; this.vY = 0; } - + else { if (Game.graphicalEffects) { - if (this.effect.equals(BulletEffect.trail) || this.effect.equals(BulletEffect.fire)) - Game.effects.add(new Effect(this.posX, this.posY, Effect.EffectType.trail)); + if (this.effect.equals(BulletEffect.trail) || this.effect.equals(BulletEffect.fire) || this.effect.equals(BulletEffect.darkFire)) + Game.effects.add(Effect.createNewEffect(this.posX, this.posY, Effect.EffectType.trail)); if (this.effect.equals(BulletEffect.fireTrail)) - Game.effects.add(new Effect(this.posX, this.posY, Effect.EffectType.smokeTrail)); + { + Game.effects.add(Effect.createNewEffect(this.posX - this.vX * Panel.frameFrequency / 8, this.posY - this.vY * Panel.frameFrequency / 8, Effect.EffectType.smokeTrail, 0.25, 0.75)); + Game.effects.add(Effect.createNewEffect(this.posX - this.vX * Panel.frameFrequency / 4, this.posY - this.vY * Panel.frameFrequency / 4, Effect.EffectType.smokeTrail, 0.25, 0.50)); + Game.effects.add(Effect.createNewEffect(this.posX - this.vX * Panel.frameFrequency / 8 * 3, this.posY - this.vY * Panel.frameFrequency / 8 * 3, Effect.EffectType.smokeTrail, 0.25, 0.25)); + Game.effects.add(Effect.createNewEffect(this.posX, this.posY, Effect.EffectType.smokeTrail, 0.25, 0)); + } if (this.effect.equals(BulletEffect.fire) || this.effect.equals(BulletEffect.fireTrail)) - Game.effects.add(new Effect(this.posX, this.posY, Effect.EffectType.fire)); + { + Game.effects.add(Effect.createNewEffect(this.posX - this.vX * Panel.frameFrequency / 8, this.posY - this.vY * Panel.frameFrequency / 8, Effect.EffectType.fire, 0.25, 0.75)); + Game.effects.add(Effect.createNewEffect(this.posX - this.vX * Panel.frameFrequency / 4, this.posY - this.vY * Panel.frameFrequency / 4, Effect.EffectType.fire, 0.25, 0.50)); + Game.effects.add(Effect.createNewEffect(this.posX - this.vX * Panel.frameFrequency / 8 * 3, this.posY - this.vY * Panel.frameFrequency / 8 * 3, Effect.EffectType.fire, 0.25, 0.25)); + Game.effects.add(Effect.createNewEffect(this.posX, this.posY, Effect.EffectType.fire, 0.25, 0)); + } + + if (this.effect.equals(BulletEffect.darkFire)) + { + Game.effects.add(Effect.createNewEffect(this.posX - this.vX * Panel.frameFrequency / 8, this.posY - this.vY * Panel.frameFrequency / 8, Effect.EffectType.darkFire, 0.25, 0.75)); + Game.effects.add(Effect.createNewEffect(this.posX - this.vX * Panel.frameFrequency / 4, this.posY - this.vY * Panel.frameFrequency / 4, Effect.EffectType.darkFire, 0.25, 0.50)); + Game.effects.add(Effect.createNewEffect(this.posX - this.vX * Panel.frameFrequency / 8 * 3, this.posY - this.vY * Panel.frameFrequency / 8 * 3, Effect.EffectType.darkFire, 0.25, 0.25)); + Game.effects.add(Effect.createNewEffect(this.posX, this.posY, Effect.EffectType.darkFire, 0.25, 0)); + } } } @@ -265,8 +296,12 @@ public void update() public void draw(Graphics p) { double opacity = ((60 - destroyTimer) / 60.0); - p.setColor(new Color(this.color.getRed(), this.color.getGreen(), this.color.getBlue(), (int)(opacity * opacity * opacity * 255.0))); - Window.fillOval(p, posX, posY, size + destroyTimer * (size / Bullet.bullet_size), size + destroyTimer * (size / Bullet.bullet_size)); + double sizeModifier = destroyTimer * (size / Bullet.bullet_size); + p.setColor(new Color(this.outlineColor.getRed(), this.outlineColor.getGreen(), this.outlineColor.getBlue(), (int)(opacity * opacity * opacity * 255.0))); + Window.fillOval(p, posX, posY, size + sizeModifier, size + sizeModifier); + p.setColor(new Color(this.baseColor.getRed(), this.baseColor.getGreen(), this.baseColor.getBlue(), (int)(opacity * opacity * opacity * 255.0))); + Window.fillOval(p, posX, posY, (size + sizeModifier) * 0.6, (size + sizeModifier) * 0.6); + } } diff --git a/src/tanks/Flame.java b/src/tanks/BulletFlame.java old mode 100755 new mode 100644 similarity index 85% rename from src/tanks/Flame.java rename to src/tanks/BulletFlame.java index 3f04921d..9b203a34 --- a/src/tanks/Flame.java +++ b/src/tanks/BulletFlame.java @@ -3,15 +3,15 @@ import java.awt.Color; import java.awt.Graphics; -public class Flame extends Bullet +public class BulletFlame extends Bullet { double life = 100; double age = 0; double frequency = Panel.frameFrequency; - public Flame(double x, double y, Color color, int bounces, Tank t) + public BulletFlame(double x, double y, int bounces, Tank t) { - super(x, y, color, bounces, t); + super(x, y, bounces, t); t.liveBullets--; this.useCustomWallCollision = true; } diff --git a/src/tanks/BulletLaser.java b/src/tanks/BulletLaser.java new file mode 100644 index 00000000..9927d8ac --- /dev/null +++ b/src/tanks/BulletLaser.java @@ -0,0 +1,39 @@ +package tanks; + +import java.awt.Color; + +public class BulletLaser extends Bullet +{ + public BulletLaser(double x, double y, int bounces, Tank t) + { + super(x, y, bounces, t); + t.liveBullets--; + this.baseColor = Color.red; + } + + public void shoot() + { + while(!this.destroy) + { + if (ScreenGame.finished) + this.destroy = true; + + this.update(); + Game.effects.add(Effect.createNewEffect(this.posX, this.posY, Effect.EffectType.laser)); + } + + if (Game.graphicalEffects) + { + for (int i = 0; i < this.size * 4; i++) + { + Effect e = Effect.createNewEffect(this.posX, this.posY, Effect.EffectType.piece); + int var = 50; + e.maxAge /= 2; + e.col = new Color((int) Math.min(255, Math.max(0, this.baseColor.getRed() + Math.random() * var - var / 2)), (int) Math.min(255, Math.max(0, this.baseColor.getGreen() + Math.random() * var - var / 2)), (int) Math.min(255, Math.max(0, this.baseColor.getBlue() + Math.random() * var - var / 2))); + e.setPolarMotion(Math.random() * 2 * Math.PI, Math.random() * this.size / 50.0 * 4); + Game.effects.add(e); + } + } + } + +} diff --git a/src/tanks/Button.java b/src/tanks/Button.java index 3234c16d..3485c91d 100755 --- a/src/tanks/Button.java +++ b/src/tanks/Button.java @@ -1,7 +1,6 @@ package tanks; import java.awt.Color; -import java.awt.Font; import java.awt.Graphics; public class Button @@ -12,77 +11,87 @@ public class Button public double sizeX; public double sizeY; public String text; - + public boolean enableHover = false; public String[] hoverText; - + public boolean selected = false; - + public boolean clicked = false; - + Color unselectedCol = new Color(255, 255, 255); Color selectedCol = new Color(240, 240, 255); - public Button(double sX, double sY, String text, Runnable f) + public Button(double x, double y, double sX, double sY, String text, Runnable f) { this.function = f; - + + this.posX = x; + this.posY = y; this.sizeX = sX; this.sizeY = sY; this.text = text; } - - public Button(double sX, double sY, String text, Runnable f, String hoverText) + + public Button(double x, double y, double sX, double sY, String text, Runnable f, String hoverText) { - this(sX, sY, text, f); + this(x, y, sX, sY, text, f); this.enableHover = true; this.hoverText = hoverText.split("---"); } - - public void draw(Graphics g, int x, int y) + + public void draw(Graphics g) { - this.posX = x; - this.posY = y; - - g.setFont(g.getFont().deriveFont(Font.BOLD, (float) (24 * Window.scale))); - + Window.setInterfaceFontSize(g, 24); + if (selected) g.setColor(this.selectedCol); else g.setColor(this.unselectedCol); - - Window.fillRect(g, posX, posY, sizeX, sizeY); - + + Window.fillInterfaceRect(g, posX, posY, sizeX, sizeY); + g.setColor(Color.black); - Window.drawText(g, posX, posY + 5, text); - - if (selected && enableHover) + Window.drawInterfaceText(g, posX, posY + 5, text); + + if (enableHover) { - Window.drawTooltip(g, this.hoverText); + if (selected) + { + g.setColor(Color.blue); + Window.fillInterfaceOval(g, this.posX + this.sizeX / 2 - this.sizeY / 2, this.posY, this.sizeY * 3 / 4, this.sizeY * 3 / 4); + g.setColor(Color.white); + Window.drawInterfaceText(g, this.posX + this.sizeX / 2 - this.sizeY / 2, this.posY + 5, "i"); + Window.drawTooltip(g, this.hoverText); + } + else + { + g.setColor(new Color(0, 150, 255)); + Window.fillInterfaceOval(g, this.posX + this.sizeX / 2 - this.sizeY / 2, this.posY, this.sizeY * 3 / 4, this.sizeY * 3 / 4); + g.setColor(Color.white); + Window.drawInterfaceText(g, this.posX + this.sizeX / 2 - this.sizeY / 2, this.posY + 5, "i"); + } } } - - public void update(int x, int y) + + public void update() { - this.posX = x; - this.posY = y; + double mx = Game.window.getInterfaceMouseX(); + double my = Game.window.getInterfaceMouseY(); - double mx = Game.window.getMouseX(); - double my = Game.window.getMouseY(); - if (mx > posX - sizeX/2 && mx < posX + sizeX/2 && my > posY - sizeY/2 && my < posY + sizeY/2) selected = true; else selected = false; - - if (selected && MouseInputListener.lClickValid && !clicked) + + if (selected && InputMouse.lClickValid && !clicked) { function.run(); clicked = true; - MouseInputListener.lClickValid = false; + InputMouse.lClickValid = false; } - - if (!(selected && MouseInputListener.lClick)) + + if (!(selected && InputMouse.lClick)) clicked = false; } } diff --git a/src/tanks/Effect.java b/src/tanks/Effect.java index 3b1dede5..30f80cbc 100755 --- a/src/tanks/Effect.java +++ b/src/tanks/Effect.java @@ -5,27 +5,104 @@ public class Effect extends Movable { - public enum EffectType {fire, smokeTrail, trail, ray, mineExplosion, laser, piece, obstaclePiece, charge, tread} + public enum EffectType {fire, smokeTrail, trail, ray, mineExplosion, laser, piece, obstaclePiece, charge, tread, darkFire} public EffectType type; double age = 0; public Color col; - public double maxAge = Math.random() * 100 + 50; - public double targetX; - public double targetY; + public double maxAge = 100; public double size; public double ffOpacityMultiplier = Math.min(1, Panel.frameFrequency); + public boolean removed = false; + + public static Effect createNewEffect(double x, double y, EffectType type) + { + if (Game.recycleEffects.size() > 0) + { + Effect e = Game.recycleEffects.remove(0); + e.refurbish(); + e.initialize(x, y, type); + + return e; + } + else + { + Effect e = new Effect(); + e.initialize(x, y, type); + return e; + } + } + + public static Effect createNewEffect(double x, double y, EffectType type, double opacityMultiplier, double age) + { + Effect e = Effect.createNewEffect(x, y, type); + e.ffOpacityMultiplier = Math.min(1, Panel.frameFrequency * opacityMultiplier); + e.age = age * Panel.frameFrequency; + return e; + } - public Effect(double x, double y, EffectType type) + /** + * Use Effect.createNewEffect(double x, double y, Effect.EffectType type) instead of this because it can refurbish and reuse old effects + * @param x + * @param y + * @param type + */ + protected Effect() { - super(x, y); + super(0, 0); + } + + protected void initialize(double x, double y, EffectType type) + { + this.posX = x; + this.posY = y; this.type = type; - if (type.equals(EffectType.charge)) + + if (type == EffectType.fire) + this.maxAge = 20; + else if (type == EffectType.smokeTrail) + this.maxAge = 200; + else if (type == EffectType.trail) + this.maxAge = 50; + else if (type == EffectType.ray) + this.maxAge = 1; + else if (type == EffectType.mineExplosion) + this.maxAge = 20; + else if (type == EffectType.laser) + this.maxAge = 21; + else if (type == EffectType.piece) + this.maxAge = Math.random() * 100 + 50; + else if (type == EffectType.obstaclePiece) + this.maxAge = Math.random() * 100 + 50; + else if (type.equals(EffectType.charge)) { this.addPolarMotion(Math.random() * Math.PI * 2, Math.random() * 3 + 3); this.posX -= this.vX * 25; this.posY -= this.vY * 25; this.maxAge = 25; } + else if (type == EffectType.tread) + { + this.maxAge = 510; + if (Game.graphicalEffects) + this.maxAge *= 2; + } + else if (type == EffectType.darkFire) + this.maxAge = 20; + } + + protected void refurbish() + { + this.posX = 0; + this.posY = 0; + this.vX = 0; + this.vY = 0; + this.type = null; + this.age = 0; + this.col = null; + this.maxAge = Math.random() * 100 + 50; + this.size = 0; + this.ffOpacityMultiplier = Math.min(1, Panel.frameFrequency); + this.removed = false; } @Override @@ -34,17 +111,16 @@ public void checkCollision() {} @Override public void draw(Graphics p) { - //p.setColor(Color.red); - //Screen.fillRect(p, this.posX, this.posY, 4, 4); - double opacityMultiplier = Obstacle.draw_size * 1.0 / Obstacle.obstacle_size; + if (this.maxAge < this.age) + return; + + if (this.age < 0) + Game.exitToCrash(new RuntimeException("Effect with negative age")); + + double opacityMultiplier = ScreenGame.finishTimer / ScreenGame.finishTimerMax; if (this.type == EffectType.fire) - { - if (this.age >= 20) - { - Game.removeEffects.add(this); - return; - } + { int size = (int) (this.age * 3 + 10); double rawOpacity = (1.0 - (this.age)/20.0); rawOpacity *= rawOpacity * rawOpacity; @@ -58,8 +134,7 @@ public void draw(Graphics p) } else if (this.type == EffectType.smokeTrail) - { - + { double opacityModifier = Math.max(0, Math.min(1, this.age / 40.0 - 0.25)); int size = 20; double rawOpacity = (1.0 - (this.age)/200.0); @@ -67,23 +142,12 @@ else if (this.type == EffectType.smokeTrail) int opacity = (int)(rawOpacity * 100); Color col = new Color(0, 0, 0, Math.min(255, Math.max(0, (int) (opacity * opacityMultiplier * opacityModifier * ffOpacityMultiplier)))); - - if (opacity <= 0) - { - Game.removeEffects.add(this); - return; - } - + p.setColor(col); Window.fillOval(p, this.posX, this.posY, size, size); } else if (this.type == EffectType.trail) { - if (this.age > 50) - { - Game.removeEffects.add(this); - return; - } int size = (int)Math.min(20, this.age / 20.0 + 10); double rawOpacity = (1.0 - (this.age)/50.0); rawOpacity *= rawOpacity * rawOpacity; @@ -99,17 +163,9 @@ else if (this.type == EffectType.ray) int size = 6; p.setColor(new Color(0, 0, 0, 50)); Window.fillOval(p, this.posX, this.posY, size, size); - - Game.removeEffects.add(this); } else if (this.type == EffectType.mineExplosion) { - if (this.age > 20) - { - Game.removeEffects.add(this); - return; - } - int size = Game.tank_size * 4; int opacity = (int) (100 - this.age * 5); p.setColor(new Color(255, 0, 0, opacity)); @@ -117,82 +173,78 @@ else if (this.type == EffectType.mineExplosion) } else if (this.type == EffectType.laser) { - if (this.age > 21) - { - Game.removeEffects.add(this); - return; - } - - //int size = Bullet.bullet_size / 2; - //double size = (int) (255 - this.age * 12); double size = Bullet.bullet_size - this.age / 2; p.setColor(new Color(255, 0, 0)); Window.fillOval(p, this.posX, this.posY, size, size); } else if (this.type == EffectType.piece) { - if (this.age > this.maxAge) - { - Game.removeEffects.add(this); - return; - } - int size = 1 + (int) (Bullet.bullet_size * (1 - this.age / this.maxAge)); p.setColor(col); Window.fillOval(p, this.posX, this.posY, size, size); } else if (this.type == EffectType.obstaclePiece) { - if (this.age > this.maxAge) - { - Game.removeEffects.add(this); - return; - } - int size = 1 + (int) (Bullet.bullet_size * (1 - this.age / this.maxAge)); p.setColor(col); Window.fillRect(p, this.posX, this.posY, size, size); } else if (this.type == EffectType.charge) { - if (this.age > this.maxAge) - { - Game.removeEffects.add(this); - return; - } - int size = 1 + (int) (Bullet.bullet_size * (this.age / this.maxAge)); p.setColor(col); Window.fillOval(p, this.posX, this.posY, size, size); } else if (this.type == EffectType.tread) { - int maxAge = 510; double opacityFactor = 2; if (Game.graphicalEffects) { - maxAge *= 2; opacityFactor = 4; } - if (this.age > maxAge) - { - Game.removeBelowEffects.add(this); - return; - } - int opacity = (int) (255 - this.age / opacityFactor) / 4; p.setColor(new Color(0, 0, 0, opacity)); Window.fillRect(p, this.posX, this.posY, size * Obstacle.draw_size / Obstacle.obstacle_size, size * Obstacle.draw_size / Obstacle.obstacle_size); } + else if (this.type == EffectType.darkFire) + { + int size = (int) (this.age * 3 + 10); + double rawOpacity = (1.0 - (this.age)/20.0); + rawOpacity *= rawOpacity * rawOpacity; + int opacity = (int)(rawOpacity * 255); + + int red = Math.min(255, (int)(128 - 128.0 * (this.age / 20.0))); + Color col = new Color(red / 2, 0, red, Math.min(255, Math.max(0, (int) (opacity * opacityMultiplier * ffOpacityMultiplier)))); + + p.setColor(col); + Window.fillOval(p, this.posX, this.posY, size, size); + + } + else + { + Game.exitToCrash(new RuntimeException("Invalid effect type!")); + } } @Override public void update() - { + { this.posX += this.vX * Panel.frameFrequency; this.posY += this.vY * Panel.frameFrequency; this.age += Panel.frameFrequency; + //this.age++; + + if (this.age > this.maxAge && !removed) + { + removed = true; + + if (Game.effects.contains(this)) + Game.removeEffects.add(this); + + else if (Game.belowEffects.contains(this)) + Game.removeBelowEffects.add(this); + } } } diff --git a/src/tanks/EnemyTank.java b/src/tanks/EnemyTank.java index 1d78dfcf..3041d45e 100755 --- a/src/tanks/EnemyTank.java +++ b/src/tanks/EnemyTank.java @@ -3,17 +3,16 @@ import java.awt.Color; import java.util.ArrayList; -/** This class is the skeleton tank class. +/** This class is the 'skeleton' tank class. * It can be extended and values can be changed to easily produce an AI for another tank. * Also, the behavior is split into many methods which are intended to be overridden easily.*/ public class EnemyTank extends Tank { - /** Determines which type of AI the tank will use when shooting. - * Straight means that the tank will shoot directly at the player if the player is in line of sight. - * Reflect means that the tank will use a Ray with reflections to find possible ways to hit the player. - * Alternate means that the tank will switch between shooting straight at the player and using the reflect AI with every shot. - * Wander means that the tank will randomly rotate and shoot only if it detects the player*/ + * Straight means that the tank will shoot directly at the target enemy if the target enemy is in line of sight. + * Reflect means that the tank will use a Ray with reflections to find possible ways to hit the target enemy. + * Alternate means that the tank will switch between shooting straight at the target enemy and using the reflect AI with every shot. + * Wander means that the tank will randomly rotate and shoot only if it detects the target enemy*/ public enum ShootAI {wander, straight, alternate, reflect} /** The type which shows what direction the tank is moving. Clockwise and Counter Clockwise are for idle, while Aiming is for when the tank aims.*/ @@ -25,18 +24,18 @@ protected enum RotationPhase {clockwise, counterClockwise, aiming} // These values do not change normally along the course of the game. public boolean enableMovement = true; - /** When set to true, will call reactToPlayerSight() when an unobstructed line of sight to the player can be made */ - public boolean enablePlayerReaction = true; + /** When set to true, will call reactToTargetEnemySight() when an unobstructed line of sight to the target enemy can be made */ + public boolean enableTargetEnemyReaction = true; public boolean enableMineLaying = true; public boolean enableMineAvoidance = true; public boolean enableBulletAvoidance = true; - /** When set to true, will calculate player velocity when shooting. Only effective when shootAIType is straight!*/ + /** When set to true, will calculate target enemy velocity when shooting. Only effective when shootAIType is straight!*/ public boolean enablePredictiveFiring = true; /** When set to true, will shoot at bullets aiming towards the tank*/ public boolean enableDefensiveFiring = false; - /** When set to true, will shoot a ray at the player and enable reactions when the player is in sight*/ - public boolean enableLookingAtPlayer = true; - + /** When set to true, will shoot a ray at the target enemy and enable reactions when the target enemy is in sight*/ + public boolean enableLookingAtTargetEnemy = true; + public int bulletBounces = 1; public Color bulletColor = Color.blue; public double bulletSize = Bullet.bullet_size; @@ -46,18 +45,18 @@ protected enum RotationPhase {clockwise, counterClockwise, aiming} /** Larger values decrease accuracy but make the tank behavior more unpredictable*/ public double aimAccuracyOffset = 0.2; - /** Threshold angle difference needed between angle and aimAngle to count as touching the player*/ + /** Threshold angle difference needed between angle and aimAngle to count as touching the target enemy*/ public double aimThreshold = 0.1; - + /** Minimum time to randomly change idle direction, added to turretIdleTimerRandom * Math.random()*/ public double turretIdleTimerBase = 25; /** Random factor in calculating time to randomly change idle direction, multiplied by Math.random() and added to turretIdleTimerBase*/ public double turretIdleTimerRandom = 500; /** Minimum time to lay a mine, added to mineTimerRandom * Math.random()*/ - public double mineTimerBase = 2000; + public double mineTimerBase = 1000; /** Random factor in calculating time to lay a mine, multiplied by Math.random() and added to mineTimerBase*/ - public double mineTimerRandom = 2000; + public double mineTimerRandom = 4000; /** Minimum time in between shooting bullets, added to cooldownRandom * Math.random()*/ public double cooldownBase = 60; @@ -67,15 +66,17 @@ protected enum RotationPhase {clockwise, counterClockwise, aiming} /** Time waited when changing direction of motion*/ public double directionChangeCooldown = 15; - /** Speed at which the turret moves while aiming at a player*/ + /** Speed at which the turret moves while aiming at a target enemy*/ public double aimTurretSpeed = 0.03; /** Speed at which the turret moves while idle*/ public double idleTurretSpeed = 0.005; /** Speed at which the tank moves*/ public double speed = 2.5; + /** Chance per frame to change direction*/ - public double motionChangeChance = 0.001; + public double motionChangeChance = 0.01; + /** Time which the tank will avoid a bullet after the bullet is no longer aiming at the tank*/ public double avoidTimerBase = 30; @@ -89,7 +90,7 @@ protected enum RotationPhase {clockwise, counterClockwise, aiming} // The following are values which are internally used for carrying out behavior. // These values change constantly during the course of the game. - /** Used for tanks which do not use the straight AI, when detecting the player with a ray. Tells the tank to aim towards the found target angle.*/ + /** Used for tanks which do not use the straight AI, when detecting the target enemy with a ray. Tells the tank to aim towards the found target angle.*/ protected boolean aim = false; /** True for when a tank just laid a mine*/ @@ -98,8 +99,8 @@ protected enum RotationPhase {clockwise, counterClockwise, aiming} /** Alternates for tanks with the alternate AI. Tells tanks to shoot with reflection and then to shoot straight.*/ protected boolean straightShoot = false; - /** If a direct line of sight to the player exists, set to true*/ - protected boolean seesPlayer = false; + /** If a direct line of sight to the target enemy exists, set to true*/ + protected boolean seesTargetEnemy = false; /** Age in frames*/ protected double age = 0; @@ -107,18 +108,21 @@ protected enum RotationPhase {clockwise, counterClockwise, aiming} /** Stores distances to obstacles or tanks in 8 directions*/ protected int[] distances = new int[8]; - /** Used only in non-straight AI tanks. When detecting the player, set to the angle necessary to hit them. This angle is added to random offsets to search for the player moving.*/ + /** Used only in non-straight AI tanks. When detecting the target enemy, set to the angle necessary to hit them. This angle is added to random offsets to search for the target enemy moving.*/ protected double lockedAngle = 0; - /** Used only in non-straight AI tanks. Angle at which the tank is searching with its aim ray for the player*/ + /** Used only in non-straight AI tanks. Angle at which the tank is searching with its aim ray for the target enemy*/ protected double searchAngle = 0; /** Angle at which the tank aims after having found its target (if non-straight AI, found with a ray, otherwise just the angle to the tank)*/ protected double aimAngle = 0; - /** Direction in which the tank moves*/ + /** Direction in which the tank moves when idle*/ protected double direction = ((int)(Math.random() * 8))/2.0; + /** When enabled, the current motion direction will be kept until the tank decides to change direction*/ + protected boolean overrideDirection = false; + /** Direction in which the tank moves to avoid a bullet that will hit it*/ protected double avoidDirection = 0; @@ -126,7 +130,7 @@ protected enum RotationPhase {clockwise, counterClockwise, aiming} protected double idleTimer = (Math.random() * turretIdleTimerRandom) + turretIdleTimerBase; /** Time between shooting bullets*/ - protected double cooldown = 100; + protected double cooldown = 200; /** Time until the next mine will be laid*/ protected double mineTimer = (Math.random() * mineTimerBase + mineTimerRandom); @@ -139,10 +143,10 @@ protected enum RotationPhase {clockwise, counterClockwise, aiming} /** Nearest bullet aiming at this tank, if avoid timer is > than 0*/ protected Bullet nearestBullet; - + /** Disable offset to shoot a bullet*/ public boolean disableOffset = false; - + /** Direction added to the bullet's direction to flee a bullet, possibly mirrored*/ protected double fleeDirection = Math.PI / 2; @@ -153,7 +157,10 @@ protected enum RotationPhase {clockwise, counterClockwise, aiming} protected RotationPhase idlePhase = RotationPhase.clockwise; /** Time until the tank will continue motion*/ - protected double motionTimer = 0; + protected double motionPauseTimer = 0; + + /** Normally the nearest tank not on this tank's team. This is the tank that this tank will fight*/ + protected Tank targetEnemy; public EnemyTank(String name, double x, double y, int size, Color color, double angle, ShootAI ai) { @@ -169,6 +176,33 @@ public EnemyTank(String name, double x, double y, int size, Color color, double this.shootAIType = ai; } + @Override + public void update() + { + this.angle = this.angle % (Math.PI * 2); + + this.age += Panel.frameFrequency; + + if (!this.destroy) + { + if (this.shootAIType != ShootAI.wander) + this.updateTarget(); + + if (this.enableMovement) + this.updateMotionAI(); + + if (!ScreenGame.finished) + { + this.updateTurretAI(); + this.updateMineAI(); + } + + this.postUpdate(); + } + + super.update(); + } + /** Prepare to fire a bullet*/ @Override public void shoot() @@ -186,12 +220,13 @@ public void shoot() offset = 0; this.disableOffset = false; } - + Ray a = new Ray(this.posX, this.posY, this.angle + offset, this.bulletBounces, this); a.moveOut(5); - + Movable m = a.getTarget(); - if (!(m instanceof Tank && !m.equals(Game.player))) + + if (!Team.isAllied(this, m)) { this.launchBullet(offset); } @@ -201,13 +236,15 @@ public void shoot() /** Actually fire a bullet*/ public void launchBullet(double offset) { - Bullet b = new Bullet(this.posX, this.posY, this.bulletColor, this.bulletBounces, this); + Window.playSound("resources/shoot.wav"); + + Bullet b = new Bullet(this.posX, this.posY, this.bulletBounces, this); b.setPolarMotion(angle + offset, this.bulletSpeed); b.moveOut((int) (25 / this.bulletSpeed * 2 * this.size / Game.tank_size)); b.effect = this.bulletEffect; b.size = this.bulletSize; b.damage = this.bulletDamage; - + Game.movables.add(b); this.cooldown = (int) (Math.random() * this.cooldownRandom + this.cooldownBase); @@ -215,23 +252,27 @@ public void launchBullet(double offset) this.straightShoot = !this.straightShoot; } - @Override - public void update() + public void updateTarget() { - this.angle = this.angle % (Math.PI * 2); - - this.age += Panel.frameFrequency; + double nearestDist = Double.MAX_VALUE; + Movable nearest = this; - if (!this.destroy) + for (int i = 0; i < Game.movables.size(); i++) { - if (this.enableMovement) - this.updateMotionAI(); - - this.updateTurretAI(); - this.updateMineAI(); + Movable m = Game.movables.get(i); + + if (m instanceof Tank && !Team.isAllied(this, m)) + { + double dist = Movable.distanceBetween(this, m); + if (dist < nearestDist) + { + nearestDist = dist; + nearest = m; + } + } } - super.update(); + this.targetEnemy = (Tank) nearest; } public void updateMotionAI() @@ -243,14 +284,15 @@ public void updateMotionAI() { this.avoidTimer -= Panel.frameFrequency; this.setPolarMotion(avoidDirection, speed); + this.overrideDirection = true; } else { fleeDirection = -fleeDirection; - if (this.seesPlayer && this.enablePlayerReaction) + if (this.seesTargetEnemy && this.enableTargetEnemyReaction) { - this.reactToPlayerSight(); + this.reactToTargetEnemySight(); } else { @@ -260,15 +302,20 @@ public void updateMotionAI() } - public void reactToPlayerSight() + public void reactToTargetEnemySight() { - this.setMotionInDirection(Game.player.posX, Game.player.posY, speed); + this.overrideDirection = true; + this.setMotionInDirection(targetEnemy.posX, targetEnemy.posY, speed); } public void updateIdleMotion() { if (Math.random() < this.motionChangeChance || this.hasCollided) { + this.overrideDirection = false; + + double prevDirection = this.direction; + ArrayList directions = new ArrayList(); for (double dir = 0; dir < 4; dir += 0.5) @@ -294,19 +341,23 @@ public void updateIdleMotion() else this.direction = directions.get(chosenDir); - this.motionTimer = this.directionChangeCooldown; + if (this.direction != prevDirection) + this.motionPauseTimer = this.directionChangeCooldown; } - if (this.motionTimer > 0) + if (this.motionPauseTimer > 0) { this.vX = 0; this.vY = 0; - this.motionTimer = (Math.max(0, this.motionTimer - Panel.frameFrequency)); + this.motionPauseTimer = (Math.max(0, this.motionPauseTimer - Panel.frameFrequency)); } else - { - this.setPolarMotion(this.direction / 2 * Math.PI, speed); - this.addIdleMotionOffset(); + { + if (!this.overrideDirection) + { + this.setPolarMotion(this.direction / 2 * Math.PI, speed); + this.addIdleMotionOffset(); + } } } @@ -384,8 +435,8 @@ public void checkForBulletThreats() public void updateTurretAI() { - if (this.enableLookingAtPlayer) - this.lookAtPlayer(); + if (this.enableLookingAtTargetEnemy) + this.lookAtTargetEnemy(); if (this.shootAIType.equals(ShootAI.wander)) this.updateTurretWander(); @@ -396,7 +447,7 @@ else if (this.shootAIType.equals(ShootAI.straight)) this.cooldown -= Panel.frameFrequency; } - + public void updateTurretWander() { Ray a = new Ray(this.posX, this.posY, this.angle, this.bulletBounces, this); @@ -405,7 +456,7 @@ public void updateTurretWander() Movable m = a.getTarget(); if (!(m == null)) - if (m.equals(Game.player)) + if (!Team.isAllied(m, this) && m instanceof Tank) this.shoot(); if (this.idlePhase == RotationPhase.clockwise) @@ -436,47 +487,48 @@ public void updateTurretStraight() } else if (this.enablePredictiveFiring) { - Ray r = new Ray(Game.player.posX, Game.player.posY, Game.player.getPolarDirection(), 0, Game.player, Game.tank_size); + Ray r = new Ray(targetEnemy.posX, targetEnemy.posY, targetEnemy.getPolarDirection(), 0, targetEnemy, Game.tank_size); r.size = Game.tank_size; - + this.disableOffset = false; if (r.getDist() > 2) { - this.aimAngle = this.getAngleInDirection(Game.player.posX + Game.player.vX * Movable.distanceBetween(this, Game.player) / this.bulletSpeed, Game.player.posY + Game.player.vY * Movable.distanceBetween(this, Game.player) / this.bulletSpeed); + this.aimAngle = this.getAngleInDirection(targetEnemy.posX + targetEnemy.vX * Movable.distanceBetween(this, targetEnemy) / this.bulletSpeed, targetEnemy.posY + targetEnemy.vY * Movable.distanceBetween(this, targetEnemy) / this.bulletSpeed); } else { - this.aimAngle = this.getAngleInDirection(Game.player.posX, Game.player.posY); + this.aimAngle = this.getAngleInDirection(targetEnemy.posX, targetEnemy.posY); } } else { - this.aimAngle = this.getAngleInDirection(Game.player.posX, Game.player.posY); + this.aimAngle = this.getAngleInDirection(targetEnemy.posX, targetEnemy.posY); this.disableOffset = false; } - - double a = this.getAngleInDirection(Game.player.posX, Game.player.posY); + + double a = this.getAngleInDirection(targetEnemy.posX, targetEnemy.posY); Ray r = new Ray(this.posX, this.posY, a, 0, this); r.moveOut(5); Movable m = r.getTarget(); - + if (m != null) - if (m.equals(Game.player)) + if (m.equals(this.targetEnemy)) this.shoot(); + if (Math.abs(this.aimAngle - this.angle) > this.aimThreshold / 2) { if ((this.angle - this.aimAngle + Math.PI * 3) % (Math.PI*2) - Math.PI < 0) this.angle += this.aimTurretSpeed * Panel.frameFrequency; else this.angle -= this.aimTurretSpeed * Panel.frameFrequency; - + this.angle = this.angle % (Math.PI * 2); } - + if (Math.abs(this.angle - this.aimAngle) < this.aimThreshold && !this.disableOffset) this.angle = this.aimAngle; } @@ -527,10 +579,10 @@ else if (this.searchPhase == RotationPhase.counterClockwise) Ray ray = new Ray(this.posX, this.posY, this.searchAngle, this.bulletBounces, this); ray.moveOut(5); - + Movable target = ray.getTarget(); if (target != null) - if (target.equals(Game.player)) + if (target.equals(this.targetEnemy)) { this.lockedAngle = this.angle; this.searchPhase = RotationPhase.aiming; @@ -539,37 +591,37 @@ else if (this.searchPhase == RotationPhase.counterClockwise) } } - public void lookAtPlayer() + public void lookAtTargetEnemy() { double a; if (this.enablePredictiveFiring) - a = this.getAngleInDirection(Game.player.posX + Game.player.vX * Movable.distanceBetween(this, Game.player) / this.bulletSpeed, Game.player.posY + Game.player.vY * Movable.distanceBetween(this, Game.player) / this.bulletSpeed); + a = this.getAngleInDirection(this.targetEnemy.posX + this.targetEnemy.vX * Movable.distanceBetween(this, this.targetEnemy) / this.bulletSpeed, this.targetEnemy.posY + this.targetEnemy.vY * Movable.distanceBetween(this, this.targetEnemy) / this.bulletSpeed); else - a = this.getAngleInDirection(Game.player.posX, Game.player.posY); + a = this.getAngleInDirection(this.targetEnemy.posX, this.targetEnemy.posY); - Ray rayToPlayer = new Ray(this.posX, this.posY, a, 0, this); - rayToPlayer.moveOut(5); - Movable playerTarget = rayToPlayer.getTarget(); + Ray rayToTarget = new Ray(this.posX, this.posY, a, 0, this); + rayToTarget.moveOut(5); + Movable target = rayToTarget.getTarget(); - if (playerTarget != null) + if (target != null) { - if (playerTarget.equals(Game.player)) + if (target.equals(this.targetEnemy)) { - this.seesPlayer = true; + this.seesTargetEnemy = true; } else - this.seesPlayer = false; + this.seesTargetEnemy = false; } else - this.seesPlayer = false; + this.seesTargetEnemy = false; if (this.straightShoot) { - if (playerTarget != null) + if (target != null) { - if (playerTarget.equals(Game.player)) + if (target.equals(this.targetEnemy)) { this.aimAngle = a; } @@ -594,10 +646,10 @@ public void updateAimingTurret() double speed = this.aimTurretSpeed; if (Movable.absoluteAngleBetween(this.angle, this.aimAngle) < this.aimThreshold * 2) speed /= 2; - + if (Movable.absoluteAngleBetween(this.angle, this.aimAngle) < this.aimThreshold * 1.5) speed /= 2; - + if (Movable.angleBetween(this.angle, this.aimAngle) < 0) this.angle += speed * Panel.frameFrequency; else @@ -634,17 +686,19 @@ public void updateMineAI() double nearestTimer = 1000; Movable nearest = null; + if (!laidMine) for (int i = 0; i < Game.movables.size(); i++) { Movable m = Game.movables.get(i); - if (m instanceof Mine && Math.abs(this.posX - m.posX) < Game.tank_size * 3 && Math.abs(this.posY - m.posY) < Game.tank_size * 3) + if (m instanceof Mine && Math.pow(m.posX - this.posX, 2) + Math.pow(m.posY - this.posY, 2) <= Math.pow(200, 2)) { if (nearestX + nearestY > this.posX - m.posX + this.posY - m.posY) { nearestX = this.posX - m.posX; nearestY = this.posY - m.posY; } + if (nearestTimer > ((Mine)m).timer) { nearestTimer = ((Mine)m).timer; @@ -653,13 +707,19 @@ public void updateMineAI() } } + laidMine = false; + if (nearest != null) { if (this.enableMineAvoidance && this.enableMovement) + { this.setMotionAwayFromDirection(nearest.posX, nearest.posY, speed); + this.overrideDirection = true; + } } else { + if (this.mineTimer <= 0 && this.enableMineLaying) { boolean layMine = true; @@ -667,11 +727,11 @@ public void updateMineAI() while (i < Game.movables.size()) { Movable m = Game.movables.get(i); - if (m instanceof Tank && !m.equals(Game.player) && !m.equals(this)) + if (m instanceof Tank && Team.isAllied(this, m) && m != this) { Tank t = (Tank) m; - if (Math.abs(t.posX - this.posX) <= 200 && Math.abs(t.posY - this.posY) <= 200) - { + if (Math.pow(t.posX - this.posX, 2) + Math.pow(t.posY - this.posY, 2) <= Math.pow(200, 2)) + { layMine = false; break; } @@ -685,6 +745,7 @@ public void updateMineAI() Game.movables.add(new Mine(this.posX, this.posY, this)); this.mineTimer = (int) (Math.random() * mineTimerRandom + mineTimerBase); double angleV = this.getPolarDirection() + Math.PI + (Math.random() - 0.5) * Math.PI / 2; + this.overrideDirection = true; this.setPolarMotion(angleV, speed); laidMine = true; } @@ -696,9 +757,16 @@ public void updateMineAI() if (Math.abs(nearestX) + Math.abs(nearestY) <= 1) { + this.overrideDirection = true; this.setPolarMotion(Math.random() * 2 * Math.PI, speed); } this.mineTimer -= Panel.frameFrequency; } + + /** Called after updating but before applying motion. Intended to be overridden.*/ + public void postUpdate() + { + + } } diff --git a/src/tanks/Firework.java b/src/tanks/Firework.java index 8351cc3d..dff41131 100755 --- a/src/tanks/Firework.java +++ b/src/tanks/Firework.java @@ -95,15 +95,17 @@ public void drawUpdate(Graphics g) if (this.age >= this.maxAge) { + Window.playSound("resources/explosion.wav"); + removeList.add(this); - for (int i = 0; i < 100; i++) + for (int i = 0; i < 50; i++) { Firework e = new Firework(FireworkType.particle, this.posX, this.posY, this.list, this.removeList); e.size = 4; int var = 50; e.color = new Color((int) Math.min(255, Math.max(0, this.color.getRed() + Math.random() * var - var / 2)), (int) Math.min(255, Math.max(0, this.color.getGreen() + Math.random() * var - var / 2)), (int) Math.min(255, Math.max(0, this.color.getBlue() + Math.random() * var - var / 2))); - double power = Math.random() * 6 + 2; + double power = Math.random() * 1 + 2; e.vX = this.vX; e.vY = this.vY; e.addPolarMotion(Math.random() * 2 * Math.PI, Math.random() * power); diff --git a/src/tanks/Game.java b/src/tanks/Game.java index e96c0dd4..4b4651e1 100755 --- a/src/tanks/Game.java +++ b/src/tanks/Game.java @@ -8,7 +8,6 @@ import java.io.IOException; import java.io.PrintStream; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Date; @@ -22,13 +21,21 @@ public class Game public static ArrayList movables = new ArrayList(); public static ArrayList obstacles = new ArrayList(); - public static ArrayList effects = new ArrayList(); - public static ArrayList belowEffects = new ArrayList(); + public static ArrayList effects = new ArrayList(); + public static ArrayList belowEffects = new ArrayList(); public static ArrayList removeMovables = new ArrayList(); public static ArrayList removeObstacles = new ArrayList(); - public static ArrayList removeEffects = new ArrayList(); - public static ArrayList removeBelowEffects = new ArrayList(); + public static ArrayList removeEffects = new ArrayList(); + public static ArrayList removeBelowEffects = new ArrayList(); + + public static ArrayList recycleEffects = new ArrayList(); + + //public static Team playerTeam = new Team(new Color(0, 0, 255)); + //public static Team enemyTeam = new Team(new Color(255, 0, 0)); + + public static Team playerTeam = new Team("ally"); + public static Team enemyTeam = new Team("enemy"); static int currentSizeX = 28; static int currentSizeY = 18; @@ -37,7 +44,7 @@ public class Game public static double levelSize = 1; - public static PlayerTank player; + public static TankPlayer player; public static boolean bulletLocked = false; @@ -59,7 +66,7 @@ public class Game public static int coins = 0; public static Item[] items = new Item[5]; - public static Registry registry = new Registry(); + public static RegistryTank registry = new RegistryTank(); public static Window window; @@ -72,23 +79,23 @@ public class Game public static final String registryPath = directoryPath + "/tank-registry.txt"; public static String homedir; - public static ArrayList defaultTanks = new ArrayList(); + public static ArrayList defaultTanks = new ArrayList(); public static void initScript() { - defaultTanks.add(new Registry.DefaultTankEntry(EnemyTankBrown.class, "brown", 1)); - defaultTanks.add(new Registry.DefaultTankEntry(EnemyTankGray.class, "gray", 1)); - defaultTanks.add(new Registry.DefaultTankEntry(EnemyTankMint.class, "mint", 1.0 / 2)); - defaultTanks.add(new Registry.DefaultTankEntry(EnemyTankYellow.class, "yellow", 1.0 / 2)); - defaultTanks.add(new Registry.DefaultTankEntry(EnemyTankMagenta.class, "magenta", 1.0 / 3)); - defaultTanks.add(new Registry.DefaultTankEntry(EnemyTankRed.class, "red", 1.0 / 3)); - defaultTanks.add(new Registry.DefaultTankEntry(EnemyTankGreen.class, "green", 1.0 / 4)); - defaultTanks.add(new Registry.DefaultTankEntry(EnemyTankPurple.class, "purple", 1.0 / 4)); - defaultTanks.add(new Registry.DefaultTankEntry(EnemyTankWhite.class, "white", 1.0 / 4)); - defaultTanks.add(new Registry.DefaultTankEntry(EnemyTankOrange.class, "orange", 1.0 / 6)); - defaultTanks.add(new Registry.DefaultTankEntry(EnemyTankDarkGreen.class, "darkgreen", 1.0 / 9)); - defaultTanks.add(new Registry.DefaultTankEntry(EnemyTankBlack.class, "black", 1.0 / 10)); - defaultTanks.add(new Registry.DefaultTankEntry(EnemyTankPink.class, "pink", 1.0 / 15)); + defaultTanks.add(new RegistryTank.DefaultTankEntry(TankBrown.class, "brown", 1)); + defaultTanks.add(new RegistryTank.DefaultTankEntry(TankGray.class, "gray", 1)); + defaultTanks.add(new RegistryTank.DefaultTankEntry(TankMint.class, "mint", 1.0 / 2)); + defaultTanks.add(new RegistryTank.DefaultTankEntry(TankYellow.class, "yellow", 1.0 / 2)); + defaultTanks.add(new RegistryTank.DefaultTankEntry(TankMagenta.class, "magenta", 1.0 / 3)); + defaultTanks.add(new RegistryTank.DefaultTankEntry(TankRed.class, "red", 1.0 / 3)); + defaultTanks.add(new RegistryTank.DefaultTankEntry(TankGreen.class, "green", 1.0 / 4)); + defaultTanks.add(new RegistryTank.DefaultTankEntry(TankPurple.class, "purple", 1.0 / 4)); + defaultTanks.add(new RegistryTank.DefaultTankEntry(TankWhite.class, "white", 1.0 / 4)); + defaultTanks.add(new RegistryTank.DefaultTankEntry(TankOrange.class, "orange", 1.0 / 6)); + defaultTanks.add(new RegistryTank.DefaultTankEntry(TankDarkGreen.class, "darkgreen", 1.0 / 9)); + defaultTanks.add(new RegistryTank.DefaultTankEntry(TankBlack.class, "black", 1.0 / 10)); + defaultTanks.add(new RegistryTank.DefaultTankEntry(TankPink.class, "pink", 1.0 / 15)); homedir = System.getProperty("user.home"); if (!Files.exists(Paths.get(homedir + directoryPath))) @@ -108,7 +115,7 @@ public static void initScript() if (!Files.exists(Paths.get(homedir + registryPath))) { - Registry.initRegistry(homedir); + RegistryTank.initRegistry(homedir); } try @@ -121,7 +128,7 @@ public static void initScript() Game.logger.println(new Date().toString() + " (syswarn) logfile not found despite existence of tanks directory! using stderr instead."); } - Registry.loadRegistry(homedir); + RegistryTank.loadRegistry(homedir); } public static void main(String[] args) @@ -145,7 +152,7 @@ public void run() window = new Window(); window.setTitle("Tanks"); - window.setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("icon64.png"))); + window.setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("resources/icon64.png"))); //movables.add(new EnemyTankStationary(120, 600, tank_size)); @@ -168,6 +175,10 @@ public static void reset() belowEffects.clear(); movables.clear(); effects.clear(); + recycleEffects.clear(); + removeEffects.clear(); + removeBelowEffects.clear(); + System.gc(); start(); } @@ -179,21 +190,46 @@ public static void exit() belowEffects.clear(); movables.clear(); effects.clear(); + recycleEffects.clear(); + removeEffects.clear(); + removeBelowEffects.clear(); + System.gc(); } - public static void exitToCrash(Exception e) + public static void exit(String name) { obstacles.clear(); belowEffects.clear(); movables.clear(); effects.clear(); + recycleEffects.clear(); + removeEffects.clear(); + removeBelowEffects.clear(); + System.gc(); + + ScreenLevelBuilder s = new ScreenLevelBuilder(name); + Game.loadLevel(new File(Game.homedir + ScreenSavedLevels.levelDir + "/" + name), s); + Game.screen = s; + } + + public static void exitToCrash(Exception e) + { + obstacles.clear(); + belowEffects.clear(); + movables.clear(); + effects.clear(); e.printStackTrace(); Game.crashMessage = e.toString(); Game.logger.println(new Date().toString() + " (syserr) the game has crashed! below is a crash report, good luck:"); e.printStackTrace(Game.logger); screen = new ScreenCrashed(); + recycleEffects.clear(); + removeEffects.clear(); + removeBelowEffects.clear(); + + System.gc(); } public static void exitToTitle() @@ -210,17 +246,25 @@ public static void exitToTitle() Level.currentColor = new Color(235, 207, 166); Game.window.setScreenBounds(Game.tank_size * 28, Game.tank_size * 18); - screen = new ScreenTitle(); obstacles.clear(); belowEffects.clear(); movables.clear(); effects.clear(); + recycleEffects.clear(); + removeEffects.clear(); + removeBelowEffects.clear(); + + screen = new ScreenTitle(); System.gc(); } - public static void loadLevel(Path p) + public static void loadLevel(File f) + { + Game.loadLevel(f, null); + } + + public static void loadLevel(File f, ScreenLevelBuilder s) { - File f = p.toFile(); Scanner in; try { @@ -230,7 +274,7 @@ public static void loadLevel(Path p) { String line = in.nextLine(); Level l = new Level(line); - l.loadLevel(); + l.loadLevel(s); } } catch (FileNotFoundException e) @@ -240,12 +284,12 @@ public static void loadLevel(Path p) } public static void start() - { + { //Level level = new Level("{28,18|4...11-6,11-0...5,17...27-6,16-3...6,0...10-11,11-11...14,16...23-11,16-12...17|3-15-player,7-3-purple2-2,20-14-green,22-3-green-2,8-8.5-brown,19-8.5-mint-2,13.5-5-yellow-1}"); //System.out.println(LevelGenerator.generateLevelString()); - Registry.loadRegistry(homedir); + RegistryTank.loadRegistry(homedir); Game.currentLevel = LevelGenerator.generateLevelString(); //Game.currentLevel = "{28,18|0-17,1-16,2-15,3-14,4-13,5-12,6-11,7-10,10-7,12-5,15-2,16-1,17-0,27-0,26-1,25-2,24-3,23-4,22-5,21-6,20-7,17-10,15-12,12-15,11-16,10-17,27-17,26-16,25-15,24-14,23-13,22-12,21-11,20-10,17-7,15-5,12-2,11-1,10-0,0-0,1-1,3-3,2-2,4-4,5-5,6-6,7-7,10-10,12-12,15-15,16-16,17-17,11-11,16-11,16-6,11-6|0-8-player-0,13-8-magenta-1,14-9-magenta-3,12-10-yellow-0,15-7-yellow-2,13-0-mint-1,14-17-mint-3,27-8-mint-2,27-9-mint-2}";///LevelGenerator.generateLevelString(); diff --git a/src/tanks/KeyInputListener.java b/src/tanks/InputKeyboard.java old mode 100755 new mode 100644 similarity index 83% rename from src/tanks/KeyInputListener.java rename to src/tanks/InputKeyboard.java index 3c030857..8fb2e421 --- a/src/tanks/KeyInputListener.java +++ b/src/tanks/InputKeyboard.java @@ -4,10 +4,11 @@ import java.awt.event.KeyListener; import java.util.ArrayList; -public class KeyInputListener implements KeyListener +public class InputKeyboard implements KeyListener { public static ArrayList keys = new ArrayList(); public static ArrayList validKeys = new ArrayList(); + //public static ArrayList validChars = new ArrayList(); @Override public void keyTyped(KeyEvent e) diff --git a/src/tanks/MouseInputListener.java b/src/tanks/InputMouse.java old mode 100755 new mode 100644 similarity index 96% rename from src/tanks/MouseInputListener.java rename to src/tanks/InputMouse.java index 4985e936..7f4f3db8 --- a/src/tanks/MouseInputListener.java +++ b/src/tanks/InputMouse.java @@ -4,7 +4,7 @@ import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; -public class MouseInputListener implements MouseListener, MouseMotionListener +public class InputMouse implements MouseListener, MouseMotionListener { public static boolean lClick = false; public static boolean rClick = false; diff --git a/src/tanks/ScrollInputListener.java b/src/tanks/InputScroll.java old mode 100755 new mode 100644 similarity index 82% rename from src/tanks/ScrollInputListener.java rename to src/tanks/InputScroll.java index 6340c075..78cc1c30 --- a/src/tanks/ScrollInputListener.java +++ b/src/tanks/InputScroll.java @@ -3,7 +3,7 @@ import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; -public class ScrollInputListener implements MouseWheelListener +public class InputScroll implements MouseWheelListener { static boolean validScrollUp = false; static boolean validScrollDown = false; diff --git a/src/tanks/LaserBullet.java b/src/tanks/LaserBullet.java deleted file mode 100755 index e434869f..00000000 --- a/src/tanks/LaserBullet.java +++ /dev/null @@ -1,39 +0,0 @@ -package tanks; - -import java.awt.Color; - -public class LaserBullet extends Bullet -{ - public LaserBullet(double x, double y, Color color, int bounces, Tank t) - { - super(x, y, color, bounces, t); - t.liveBullets--; - this.color = Color.red; - } - - public void shoot() - { - while(!this.destroy) - { - if (!Game.movables.contains(Game.player) || Game.player.destroy) - this.destroy = true; - - this.update(); - Game.effects.add(new Effect(this.posX, this.posY, Effect.EffectType.laser)); - } - - if (Game.graphicalEffects) - { - for (int i = 0; i < this.size * 4; i++) - { - Effect e = new Effect(this.posX, this.posY, Effect.EffectType.piece); - int var = 50; - e.maxAge /= 2; - e.col = new Color((int) Math.min(255, Math.max(0, this.color.getRed() + Math.random() * var - var / 2)), (int) Math.min(255, Math.max(0, this.color.getGreen() + Math.random() * var - var / 2)), (int) Math.min(255, Math.max(0, this.color.getBlue() + Math.random() * var - var / 2))); - e.setPolarMotion(Math.random() * 2 * Math.PI, Math.random() * this.size / 50.0 * 4); - Game.effects.add(e); - } - } - } - -} diff --git a/src/tanks/Level.java b/src/tanks/Level.java index bbaef0ff..1095623c 100755 --- a/src/tanks/Level.java +++ b/src/tanks/Level.java @@ -1,6 +1,8 @@ package tanks; import java.awt.Color; +import java.util.ArrayList; +import java.util.HashMap; public class Level { @@ -8,20 +10,58 @@ public class Level String[] screen; String[] obstaclesPos; String[] tanks; + String[] teams; - static Color currentColor = new Color(235, 207, 166); + Team[] tankTeams; + boolean enableTeams = false; + public static Color currentColor = new Color(235, 207, 166); + public boolean editable = true; + + public HashMap teamsMap = new HashMap(); + public ArrayList teamsList = new ArrayList(); + + /** + * A level string is structured like this: + * (parentheses signify required parameters, and square brackets signify optional parameters. + * Asterisks indicate that the parameter can be repeated, separated by commas + * Do not include these in the level string.) + * {(SizeX),(SizeY),[(Red),(Green),(Blue)],[(RedNoise),(GreenNoise),(BlueNoise)]|[(ObstacleX)-(ObstacleY)]*|[(TankX)-(TankY)-(TankType)-[TankAngle]-[TeamName]]*|[(TeamName)-[FriendlyFire]-[(Red)-(Green)-(Blue)]]*} + * + */ public Level(String level) - { + { preset = level.split("\\{")[1].split("\\}")[0].split("\\|"); screen = preset[0].split(","); obstaclesPos = preset[1].split(","); tanks = preset[2].split(","); + + if (preset.length >= 4) + { + teams = preset[3].split(","); + enableTeams = true; + } + + if (screen[0].startsWith("*")) + { + editable = false; + screen[0] = screen[0].substring(1); + } + else + editable = true; } public void loadLevel() { + loadLevel(null); + } + + public void loadLevel(ScreenLevelBuilder s) + { + ScreenGame.finished = false; + ScreenGame.finishTimer = ScreenGame.finishTimerMax; + int sX = Integer.parseInt(screen[0]); int sY = Integer.parseInt(screen[1]); @@ -33,6 +73,27 @@ public void loadLevel() int dg = 20; int db = 20; + if (enableTeams) + { + tankTeams = new Team[teams.length]; + + for (int i = 0; i < teams.length; i++) + { + String[] t = teams[i].split("-"); + + if (t.length >= 5) + tankTeams[i] = new Team(t[0], Boolean.parseBoolean(t[1]), new Color(Integer.parseInt(t[2]), Integer.parseInt(t[3]), Integer.parseInt(t[4]))); + else if (t.length >= 2) + tankTeams[i] = new Team(t[0], Boolean.parseBoolean(t[1])); + else + tankTeams[i] = new Team(t[0]); + + teamsMap.put(t[0], tankTeams[i]); + + teamsList.add(tankTeams[i]); + } + } + if (screen.length >= 5) { r = Integer.parseInt(screen[2]); @@ -41,12 +102,81 @@ public void loadLevel() if (screen.length >= 8) { - dr = Integer.parseInt(screen[5]); - dg = Integer.parseInt(screen[6]); - db = Integer.parseInt(screen[7]); + dr = Math.min(255 - r, Integer.parseInt(screen[5])); + dg = Math.min(255 - g, Integer.parseInt(screen[6])); + db = Math.min(255 - b, Integer.parseInt(screen[7])); } } + if (s != null) + { + s.sizeX.inputText = sX + ""; + s.sizeY.inputText = sY + ""; + + s.width = sX; + s.height = sY; + + s.r = r; + s.g = g; + s.b = b; + s.dr = dr; + s.dg = dg; + s.db = db; + s.editable = this.editable; + Game.movables.remove(Game.player); + + s.colorRed.inputText = r + ""; + s.colorGreen.inputText = g + ""; + s.colorBlue.inputText = b + ""; + s.colorVarRed.inputText = dr + ""; + s.colorVarGreen.inputText = dg + ""; + s.colorVarBlue.inputText = db + ""; + + s.colorVarRed.maxValue = 255 - r; + s.colorVarGreen.maxValue = 255 - g; + s.colorVarBlue.maxValue = 255 - b; + + if (!editable) + { + s.play.posY += 60; + s.quit.posY -= 60; + } + + if (!enableTeams) + { + this.teamsList.add(Game.playerTeam); + this.teamsList.add(Game.enemyTeam); + } + + for (int i = 0; i < this.teamsList.size(); i++) + { + final int j = i; + Team t = this.teamsList.get(i); + Button buttonToAdd = new Button(0, 0, 350, 40, t.name, new Runnable() + { + @Override + public void run() + { + s.teamName.inputText = t.name; + s.lastTeamButton = j; + s.editTeamMenu = true; + s.selectedTeam = t; + if (s.selectedTeam.friendlyFire) + s.teamFriendlyFire.text = "Friendly fire: on"; + else + s.teamFriendlyFire.text = "Friendly fire: off"; + } + } + ); + s.teamButtons.add(buttonToAdd); + + } + + s.teams = this.teamsList; + + s.sortButtons(); + } + Game.currentSizeX = (int) (sX * Game.bgResMultiplier); Game.currentSizeY = (int) (sY * Game.bgResMultiplier); @@ -62,7 +192,7 @@ public void loadLevel() } Game.window.setScreenBounds(Game.tank_size * sX, Game.tank_size * sY); - + if (!((obstaclesPos.length == 1 && obstaclesPos[0].equals("")) || obstaclesPos.length == 0)) { for (int i = 0; i < obstaclesPos.length; i++) @@ -103,24 +233,40 @@ public void loadLevel() } } - for (int i = 0; i < tanks.length; i++) + if (!preset[2].equals("")) { - String[] tank = tanks[i].split("-"); - double x = Game.tank_size * (0.5 + Double.parseDouble(tank[0])); - double y = Game.tank_size * (0.5 + Double.parseDouble(tank[1])); - String type = tank[2].toLowerCase(); - double angle = 0; - if (tank.length == 4) - angle = (Math.PI / 2 * Double.parseDouble(tank[3])); - - if (type.equals("player")) + for (int i = 0; i < tanks.length; i++) { - Game.player = new PlayerTank(x, y, angle); - Game.movables.add(Game.player); - } - else - { - Game.movables.add(Game.registry.getEntry(type).getTank(x, y, angle)); + String[] tank = tanks[i].split("-"); + double x = Game.tank_size * (0.5 + Double.parseDouble(tank[0])); + double y = Game.tank_size * (0.5 + Double.parseDouble(tank[1])); + String type = tank[2].toLowerCase(); + double angle = 0; + if (tank.length >= 4) + angle = (Math.PI / 2 * Double.parseDouble(tank[3])); + + Tank t; + if (type.equals("player")) + { + t = new TankPlayer(x, y, angle); + Game.player = (TankPlayer) t; + t.team = Game.playerTeam; + } + else + { + t = Game.registry.getEntry(type).getTank(x, y, angle); + t.team = Game.enemyTeam; + } + + if (enableTeams) + { + if (tank.length >= 5) + t.team = teamsMap.get(tank[4]); + else + t.team = null; + } + + Game.movables.add(t); } } } diff --git a/src/tanks/LevelGenerator.java b/src/tanks/LevelGenerator.java index 60b4228e..0fed46a7 100755 --- a/src/tanks/LevelGenerator.java +++ b/src/tanks/LevelGenerator.java @@ -5,7 +5,6 @@ public class LevelGenerator { public static String generateLevelString() { - //int type = (int) (Math.random() * 13); //test ^ double size = Game.levelSize; @@ -25,7 +24,7 @@ public static String generateLevelString() int g = (int)(Math.random() * 50) + 185; int b = (int)(Math.random() * 50) + 185; - String s = "{" + width + "," + height + "," + r + "," + g + "," + b + ",20,20,20|"; + String s = "{*" + width + "," + height + "," + r + "," + g + "," + b + ",20,20,20|"; boolean[][] cells = new boolean[width][height]; for (int i = 0; i < cells.length; i++) { @@ -64,7 +63,9 @@ public static String generateLevelString() if (i == walls - 1) { s += "|"; - } else { + } + else + { s += ","; } } @@ -85,11 +86,13 @@ public static String generateLevelString() cells[Math.max(0, Math.min(width - 1, x+i))][Math.max(0, Math.min(height - 1, y+j))] = true; s += x + "-" + y + "-player-" + (int)(Math.random() * 4) + ","; - for (int i = 0; i < numTanks; i++) { + for (int i = 0; i < numTanks; i++) + { int angle = (int) (Math.random() * 4); x = (int) (Math.random() * (width)); y = (int) (Math.random() * (height)); - while (cells[x][y]) { + while (cells[x][y]) + { x = (int) (Math.random() * (width)); y = (int) (Math.random() * (height)); } diff --git a/src/tanks/Mine.java b/src/tanks/Mine.java index 5ad36227..3bc74dcf 100755 --- a/src/tanks/Mine.java +++ b/src/tanks/Mine.java @@ -8,7 +8,8 @@ public class Mine extends Movable public static int mine_size = 30; public double timer = 1000; public int size = mine_size; - + public Color outlineColor; + public Tank tank; public Mine(double x, double y, Tank t) @@ -16,6 +17,8 @@ public Mine(double x, double y, Tank t) super(x, y); tank = t; t.liveMines++; + this.team = t.team; + this.outlineColor = Team.getObjectColor(t.color, t); } @Override @@ -23,18 +26,16 @@ public void checkCollision() { } @Override public void draw(Graphics p) - { - int s = (int) (this.size); - - //p.setColor(Color.yellow); - //if (timer < 150 && timer % 2 == 1) - // p.setColor(Color.red); + { + p.setColor(this.outlineColor); + Window.fillOval(p, this.posX, this.posY, this.size, this.size); + p.setColor(new Color(255, (int) ((this.timer) / 1000.0 * 255), 0)); if (timer < 150 && ((int) timer % 16) / 8 == 1) p.setColor(Color.yellow); - Window.fillOval(p, this.posX, this.posY, s, s); + Window.fillOval(p, this.posX, this.posY, this.size * 0.8, this.size * 0.8); } @Override @@ -64,14 +65,16 @@ public void update() public void explode() { + Window.playSound("resources/explosion.wav"); + for (int i = 0; i < Game.movables.size(); i++) { if (Game.graphicalEffects) { - for (int j = 0; j < 100; j++) + for (int j = 0; j < 50; j++) { double random = Math.random(); - Effect e = new Effect(this.posX, this.posY, Effect.EffectType.piece); + Effect e = Effect.createNewEffect(this.posX, this.posY, Effect.EffectType.piece); e.maxAge /= 2; e.col = new Color(255, (int) ((1 - random) * 155 + Math.random() * 100), 0); e.setPolarMotion(Math.random() * 2 * Math.PI, random * 4); @@ -86,20 +89,22 @@ public void explode() if (o instanceof Tank && !o.destroy) { this.destroy = true; - this.vX = 0; - this.vY = 0; - ((Tank) o).lives -= 2; - ((Tank)o).flashAnimation = 1; - if (((Tank)o).lives <= 0) + if (!(Team.isAllied(this, o) && !this.team.friendlyFire)) { - ((Tank)o).flashAnimation = 0; - o.destroy = true; - if (o.equals(Game.player)) - Game.coins -= 5; - if (this.tank.equals(Game.player)) - Game.coins += ((Tank)o).coinValue; - } + ((Tank) o).lives -= 2; + ((Tank)o).flashAnimation = 1; + + if (((Tank)o).lives <= 0) + { + ((Tank)o).flashAnimation = 0; + o.destroy = true; + if (o.equals(Game.player)) + Game.coins -= 5; + if (this.tank.equals(Game.player)) + Game.coins += ((Tank)o).coinValue; + } + } } else if (o instanceof Mine && !o.destroy) { @@ -108,23 +113,23 @@ else if (o instanceof Mine && !o.destroy) } } - for (int i = 0; i < Game.obstacles.size(); i++) - { - Obstacle o = Game.obstacles.get(i); - if (Math.pow(Math.abs(o.posX - this.posX), 2) + Math.pow(Math.abs(o.posY - this.posY), 2) < Math.pow(Game.tank_size * 2.5, 2)) + for (int i = 0; i < Game.obstacles.size(); i++) { - Game.removeObstacles.add(o); - - if (Game.graphicalEffects) + Obstacle o = Game.obstacles.get(i); + if (Math.pow(Math.abs(o.posX - this.posX), 2) + Math.pow(Math.abs(o.posY - this.posY), 2) < Math.pow(Game.tank_size * 2.5, 2)) { - for (int j = 0; j < Obstacle.obstacle_size - 4; j += 4) + Game.removeObstacles.add(o); + + if (Game.graphicalEffects) { - for (int k = 0; k < Obstacle.obstacle_size - 4; k += 4) + for (int j = 0; j < Obstacle.obstacle_size - 4; j += 4) { - int oX = 0; - int oY = 0; + for (int k = 0; k < Obstacle.obstacle_size - 4; k += 4) + { + int oX = 0; + int oY = 0; - /*if (j == 0) + /*if (j == 0) oX += 2; if (k == 0) oY += 2; @@ -134,25 +139,25 @@ else if (o instanceof Mine && !o.destroy) if (k == Obstacle.obstacle_size - 8) oY -= 2;*/ - Effect e = new Effect(o.posX + j + oX + 2 - Obstacle.obstacle_size / 2, o.posY + k + oY + 2 - Obstacle.obstacle_size / 2, Effect.EffectType.obstaclePiece); - e.col = o.color; + Effect e = Effect.createNewEffect(o.posX + j + oX + 2 - Obstacle.obstacle_size / 2, o.posY + k + oY + 2 - Obstacle.obstacle_size / 2, Effect.EffectType.obstaclePiece); + e.col = o.color; - double dist = Movable.distanceBetween(this, e); - double angle = this.getAngleInDirection(e.posX, e.posY); - e.addPolarMotion(angle, (200 * Math.sqrt(2) - dist) / 400 + Math.random() * 2); + double dist = Movable.distanceBetween(this, e); + double angle = this.getAngleInDirection(e.posX, e.posY); + e.addPolarMotion(angle, (200 * Math.sqrt(2) - dist) / 400 + Math.random() * 2); - Game.effects.add(e); + Game.effects.add(e); + } } } } } - } - tank.liveMines--; - Game.effects.add(new Effect(this.posX, this.posY, Effect.EffectType.mineExplosion)); + tank.liveMines--; + Game.effects.add(Effect.createNewEffect(this.posX, this.posY, Effect.EffectType.mineExplosion)); - Game.removeMovables.add(this); -} + Game.removeMovables.add(this); + } } diff --git a/src/tanks/Movable.java b/src/tanks/Movable.java index bf6fc7d4..3c02bf36 100755 --- a/src/tanks/Movable.java +++ b/src/tanks/Movable.java @@ -10,6 +10,7 @@ public abstract class Movable public double vY; public double cooldown = 0; public boolean destroy = false; + public Team team; public Movable(double x, double y) { @@ -21,8 +22,8 @@ public void update() { if (!destroy) { - this.posX += this.vX / 2 * Math.max(0, Game.tank_size - Game.player.destroyTimer) / Game.tank_size * Panel.frameFrequency; - this.posY += this.vY / 2 * Math.max(0, Game.tank_size - Game.player.destroyTimer) / Game.tank_size * Panel.frameFrequency; + this.posX += this.vX / 2 * ScreenGame.finishTimer / ScreenGame.finishTimerMax * Panel.frameFrequency; + this.posY += this.vY / 2 * ScreenGame.finishTimer / ScreenGame.finishTimerMax * Panel.frameFrequency; this.checkCollision(); } } @@ -75,7 +76,7 @@ else if (y < 0) this.vY = velY; } - + public void setMotionInDirectionWithOffset(double x, double y, double velocity, double a) { x -= this.posX; @@ -122,7 +123,7 @@ else if (y < 0) return angle; } - + public double getPolarDirection() { double x = this.vX; @@ -166,6 +167,13 @@ public void moveInDirection(double x, double y, double amount) this.posY += amount * y; } + public void drawTeam(Graphics g) + { + Window.setFontSize(g, 20); + if (this.team != null) + Window.drawText(g, this.posX, this.posY + 40, this.team.name); + } + public abstract void checkCollision(); public abstract void draw(Graphics p); @@ -174,12 +182,12 @@ public static double distanceBetween(final Movable a, final Movable b) { return Math.sqrt((a.posX-b.posX)*(a.posX-b.posX) + (a.posY-b.posY)*(a.posY-b.posY)); } - + public static double angleBetween(double a, double b) { return (a - b + Math.PI * 3) % (Math.PI*2) - Math.PI; } - + public static double absoluteAngleBetween(double a, double b) { return Math.abs((a - b + Math.PI * 3) % (Math.PI*2) - Math.PI); diff --git a/src/tanks/Panel.java b/src/tanks/Panel.java index d7d9905d..ddae07ad 100755 --- a/src/tanks/Panel.java +++ b/src/tanks/Panel.java @@ -18,10 +18,19 @@ public class Panel extends JPanel int width = Window.sizeX; boolean resize = true; + public static double windowWidth = 1400; + public static double windowHeight = 900; + + public static double restrictedWindowMouseOffsetX = 0; + public static double restrictedWindowMouseOffsetY = 0; + + static boolean showMouseTarget = true; ArrayList framesList = new ArrayList(); + public static Panel panel; + public static String winlose = ""; public static boolean win = false; @@ -49,6 +58,8 @@ public class Panel extends JPanel public Panel() { + Panel.panel = this; + timer = new Timer(0, new ActionListener() { @@ -58,7 +69,7 @@ public void actionPerformed(ActionEvent e) //long start = System.nanoTime(); try - { + { long milliTime = System.currentTimeMillis(); framesList.add(milliTime); @@ -79,8 +90,61 @@ public void actionPerformed(ActionEvent e) if (Game.coins < 0) Game.coins = 0; - Game.screen.update(); + Panel.windowWidth = Game.window.getSize().getWidth(); + Panel.windowHeight = Game.window.getSize().getHeight(); + + Window.scale = Math.min(Panel.windowWidth * 1.0 / Game.currentSizeX, (Panel.windowHeight * 1.0 - 40 - Window.yOffset) / Game.currentSizeY) / 50.0; + Window.interfaceScale = Math.min(Panel.windowWidth * 1.0 / 28, (Panel.windowHeight * 1.0 - 40 - Window.yOffset) / 18) / 50.0; + Window.unzoomedScale = Window.scale; + + if (Game.player != null && Game.screen instanceof ScreenGame && !ScreenGame.finished) + { + Window.enableMovingCamera = true; + + if (Window.movingCamera) + { + Window.playerX = Game.player.posX; + Window.playerY = Game.player.posY; + + if (Window.scale < Window.interfaceScale) + { + Window.enableMovingCamera = true; + Window.scale = Window.interfaceScale; + } + else + { + Window.enableMovingCamera = false; + } + } + } + else + { + Window.enableMovingCamera = false; + } + + if (Panel.windowWidth - Window.xOffset > Game.currentSizeX * Game.tank_size * Window.scale) + Window.enableMovingCameraX = false; + else + { + Window.enableMovingCameraX = true; + Panel.restrictedWindowMouseOffsetX = 0; + } + + if (Panel.windowHeight - Window.yOffset - 40 > Game.currentSizeY * Game.tank_size * Window.scale) + Window.enableMovingCameraY = false; + else + { + Window.enableMovingCameraY = true; + Panel.restrictedWindowMouseOffsetY = 0; + } + + Game.screen.update(); + + //long end = System.nanoTime(); + //System.out.println("Updating took: " + (end - start)); + //System.out.println(Game.effects.size()); + //System.out.println(Game.recycleEffects.size()); repaint(); @@ -120,6 +184,7 @@ public void actionPerformed(ActionEvent e) { Game.exitToCrash(exception); } + } }); @@ -128,9 +193,10 @@ public void actionPerformed(ActionEvent e) public void startTimer() { timer.start(); + new SoundThread().execute(); } - @Override + /*@Override public void paintComponent(Graphics g) { super.paintComponent(g); @@ -151,11 +217,12 @@ public void paintComponent(Graphics g) g.drawRect(15,15,(this.getWidth() - 30), this.getHeight() - 30); } - } + }*/ @Override public void paint(Graphics g) - { + { + //long start = System.nanoTime(); try { if (System.currentTimeMillis() - startTime < 1000) @@ -163,24 +230,23 @@ public void paint(Graphics g) for (int i = 0; i < Game.currentSizeX; i++) { g.setColor(Level.currentColor); - Window.fillRect(g, Window.sizeX / 2, Window.sizeY / 2, Window.sizeX * 1.2, Window.sizeY * 1.2); - g.drawImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("loading.png")), 0, 0, null); + Window.fillInterfaceRect(g, Window.sizeX / 2, Window.sizeY / 2, Window.sizeX * 1.2, Window.sizeY * 1.2); + g.drawImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("resources/loading.png")), 0, 0, null); } return; } - - Window.scale = Math.min(Game.window.getSize().getWidth() * 1.0 / Game.currentSizeX, (Game.window.getSize().getHeight() * 1.0 - 40 - Window.yOffset) / Game.currentSizeY) / 50.0; + g.setColor(new Color(174, 92, 16)); + g.fillRect(0, 0, 1 + (int)(Panel.windowWidth), 1+(int)(Panel.windowHeight)); - g.fillRect(0, 0, 1 + (int)(Game.window.getSize().getWidth()), 1+(int)(Game.window.getSize().getHeight())); - long time = (long) (System.currentTimeMillis() * frameSampling / 1000 ); if (lastFrameSec < time && lastFrameSec != firstFrameSec) { lastFPS = (int) (frames * 1.0 * frameSampling); frames = 0; } + lastFrameSec = time; frames++; @@ -191,15 +257,15 @@ public void paint(Graphics g) Game.screen.draw(g); g.setColor(new Color(87, 46, 8)); - g.fillRect(0, (int) (Game.window.getSize().getHeight() - 40 - Window.yOffset), (int) (Game.window.getSize().getWidth()), 40); + g.fillRect(0, (int) (Panel.windowHeight - 40 - Window.yOffset), (int) (Panel.windowWidth), 40); g.setColor(new Color(255, 227, 186)); g.setFont(g.getFont().deriveFont(Font.BOLD, 12)); - g.drawString("Tanks v0.4.c", 2, (int) (Game.window.getSize().getHeight() - 40 + 12 - Window.yOffset)); - g.drawString("FPS: " + lastFPS, 2, (int) (Game.window.getSize().getHeight() - 40 + 24 - Window.yOffset)); - g.drawString("Coins: " + Game.coins, 2, (int) (Game.window.getSize().getHeight() - 40 + 36 - Window.yOffset)); + g.drawString("Tanks v0.4.0", 2, (int) (Panel.windowHeight - 40 + 12 - Window.yOffset)); + g.drawString("FPS: " + lastFPS, 2, (int) (Panel.windowHeight - 40 + 24 - Window.yOffset)); + //g.drawString("Coins: " + Game.coins, 2, (int) (Panel.windowHeight - 40 + 36 - Window.yOffset)); /*int obstacles = Game.obstacles.size(); int movables = Game.movables.size(); @@ -238,19 +304,28 @@ public void paint(Graphics g) //g.fillRect(Game.gamescreen.getWidth() - 250, (int)(Game.gamescreen.getSize().getHeight() - 40 + 15 - Screen.offset), (int) (200 * (Runtime.getRuntime().totalMemory() * 1.0 / Runtime.getRuntime().maxMemory())), 10); //g.drawRect(Game.gamescreen.getWidth() - 250, (int)(Game.gamescreen.getSize().getHeight() - 40 + 15 - Screen.offset), 200, 10); - double mx = Game.window.getMouseX(); - double my = Game.window.getMouseY(); - + double mx = Game.window.getInterfaceMouseX(); + double my = Game.window.getInterfaceMouseY(); + + double mx2 = Game.window.getMouseX(); + double my2 = Game.window.getMouseY(); if (showMouseTarget) { g.setColor(Color.black); - Window.drawOval(g, mx, my, 8, 8); - Window.drawOval(g, mx, my, 4, 4); + Window.drawInterfaceOval(g, mx, my, 8, 8); + Window.drawInterfaceOval(g, mx, my, 4, 4); + + g.setColor(Color.red); + Window.drawOval(g, mx2, my2, 8, 8); + Window.drawOval(g, mx2, my2, 4, 4); } } catch (Exception e) { Game.exitToCrash(e); } + + //long end = System.nanoTime(); + //System.out.println("Drawing took: " + (end - start)); } } \ No newline at end of file diff --git a/src/tanks/Ray.java b/src/tanks/Ray.java index 2e48550c..3ad44344 100755 --- a/src/tanks/Ray.java +++ b/src/tanks/Ray.java @@ -22,6 +22,8 @@ public class Ray public Tank tank; public Tank targetTank; + + public boolean enableBullet = false; public ArrayList bounceX = new ArrayList(); public ArrayList bounceY = new ArrayList(); @@ -66,7 +68,7 @@ public Movable getTarget() while (true) { age++; - //Game.effects.add(new Effect(this.posX, this.posY, Effect.EffectType.ray)); + //Game.effects.add(Effect.createNewEffect(this.posX, this.posY, Effect.EffectType.ray)); this.posX += this.vX; this.posY += this.vY; @@ -148,7 +150,7 @@ else if (dy >= 0 && dy < bound && horizontalDist < verticalDist) } if (collided) { - if (this.bounces <= 0) + if (this.bounces <= 0 || this.age <= 1) { return null; } @@ -186,6 +188,23 @@ else if (dy >= 0 && dy < bound && horizontalDist < verticalDist) } } } + else if (Game.movables.get(i) instanceof Bullet && enableBullet) + { + Bullet t = (Bullet)(Game.movables.get(i)); + + if (Math.abs(this.posX - t.posX) < (t.size + this.size) / 2 && + Math.abs(this.posY - t.posY) < (t.size + this.size) / 2) + { + if (age * speed > Math.sqrt(2) * tank.size / 2 + 10 || skipSelfCheck) + { + //Game.effects.add(new Effect(this.posX, this.posY, Effect.EffectType.laser)); + this.targetX = this.posX; + this.targetY = this.posY; + System.out.println("hi"); + return Game.movables.get(i); + } + } + } } } } diff --git a/src/tanks/Registry.java b/src/tanks/RegistryTank.java old mode 100755 new mode 100644 similarity index 94% rename from src/tanks/Registry.java rename to src/tanks/RegistryTank.java index b5b951fd..bf0f9871 --- a/src/tanks/Registry.java +++ b/src/tanks/RegistryTank.java @@ -10,9 +10,8 @@ import java.util.Date; import java.util.Scanner; -public class Registry +public class RegistryTank { - public ArrayList tankRegistries = new ArrayList(); protected double maxTankWeight = 0; @@ -58,7 +57,7 @@ public static void loadRegistry(String homedir) ClassLoader loader = new URLClassLoader( new URL[] { new File(tankLine[3]).toURI().toURL() }); // super messy @SuppressWarnings("unchecked") Class clasz = (Class) loader.loadClass(tankLine[4]); - new Registry.TankEntry(Game.registry, clasz, tankLine[0], Double.parseDouble(tankLine[1])); + new RegistryTank.TankEntry(Game.registry, clasz, tankLine[0], Double.parseDouble(tankLine[1])); } catch (Exception e) { @@ -133,7 +132,7 @@ static class TankEntry protected double startWeight; protected double endWeight; - public TankEntry(Registry r, Class tank, String name, double weight) + public TankEntry(RegistryTank r, Class tank, String name, double weight) { this.tank = tank; this.name = name; @@ -148,14 +147,14 @@ public TankEntry(Registry r, Class tank, String name, double wei protected TankEntry() { - this.tank = EnemyTankUnknown.class; + this.tank = TankUnknown.class; this.name = "unknown"; this.weight = 0; } protected TankEntry(String name) { - this.tank = EnemyTankUnknown.class; + this.tank = TankUnknown.class; this.name = name; this.weight = 0; } @@ -200,12 +199,12 @@ public DefaultTankEntry(Class tank, String name, double weight) this.weight = weight; } - public TankEntry registerEntry(Registry r) + public TankEntry registerEntry(RegistryTank r) { return new TankEntry(r, this.tank, this.name, this.weight); } - public TankEntry registerEntry(Registry r, double weight) + public TankEntry registerEntry(RegistryTank r, double weight) { return new TankEntry(r, this.tank, this.name, weight); } diff --git a/src/tanks/Screen.java b/src/tanks/Screen.java index e066970e..503e6d6a 100755 --- a/src/tanks/Screen.java +++ b/src/tanks/Screen.java @@ -34,8 +34,8 @@ public void drawDefaultBackground(Graphics g) } } - g.setColor(new Color(0, 0, 0, (int) Panel.darkness)); - Window.fillRect(g, Window.sizeX / 2, Window.sizeY / 2, Window.sizeX, Window.sizeY); + g.setColor(new Color(0, 0, 0, Math.max(0, (int) Panel.darkness))); + Window.fillBackgroundRect(g, Window.sizeX / 2, Window.sizeY / 2, Window.sizeX, Window.sizeY); } } } diff --git a/src/tanks/ScreenAdjustWindow.java b/src/tanks/ScreenAdjustWindow.java new file mode 100644 index 00000000..28dbf702 --- /dev/null +++ b/src/tanks/ScreenAdjustWindow.java @@ -0,0 +1,20 @@ +package tanks; + +import java.awt.Graphics; + +public class ScreenAdjustWindow extends Screen +{ + + @Override + public void update() + { + + } + + @Override + public void draw(Graphics g) + { + Window.drawInterfaceText(g, Window.interfaceSizeX / 2, Window.interfaceSizeY / 2, "Use the arrow keys to fit the red squares inside the screen."); + } + +} diff --git a/src/tanks/ScreenCrashed.java b/src/tanks/ScreenCrashed.java index d7279b8d..90d58b85 100755 --- a/src/tanks/ScreenCrashed.java +++ b/src/tanks/ScreenCrashed.java @@ -6,7 +6,7 @@ public class ScreenCrashed extends Screen { - Button exit = new Button(350, 40, "Exit the game", new Runnable() + Button exit = new Button(Window.interfaceSizeX / 2, Window.interfaceSizeY - 100, 350, 40, "Exit the game", new Runnable() { @Override public void run() @@ -16,7 +16,7 @@ public void run() } ); - Button quit = new Button(350, 40, "Quit to title", new Runnable() + Button quit = new Button(Window.interfaceSizeX / 2, Window.interfaceSizeY - 160, 350, 40, "Quit to title", new Runnable() { @Override public void run() @@ -29,35 +29,35 @@ public void run() @Override public void update() { - this.quit.update(Window.sizeX / 2, Window.sizeY - 160); - this.exit.update(Window.sizeX / 2, Window.sizeY - 100); + this.quit.update(); + this.exit.update(); } @Override public void draw(Graphics g) { g.setColor(Color.blue); - Window.fillRect(g, Window.sizeX / 2, Window.sizeY / 2, Window.sizeX * 1.2, Window.sizeY * 1.2); + Window.fillInterfaceRect(g, Window.sizeX / 2, Window.sizeY / 2, Window.sizeX * 1.2, Window.sizeY * 1.2); g.setColor(Color.white); - g.setFont(g.getFont().deriveFont(Font.BOLD, (float) (100 * Window.scale))); - Window.drawText(g, 100, 100, ":("); + Window.setInterfaceFontSize(g, 100); + Window.drawInterfaceText(g, 100, 100, ":("); - g.setFont(g.getFont().deriveFont(Font.BOLD, (float) (48 * Window.scale))); - Window.drawText(g, Window.sizeX / 2, 100, "Oh noes! Tanks ran into a problem!"); + Window.setInterfaceFontSize(g, 48); + Window.drawInterfaceText(g, Window.sizeX / 2, 100, "Oh noes! Tanks ran into a problem!"); g.setFont(g.getFont().deriveFont(Font.BOLD, (float) (24 * Window.scale))); - Window.drawText(g, Window.sizeX / 2, 200, Game.crashMessage); - Window.drawText(g, Window.sizeX / 2, 280, "Check the log file for more information: "); - Window.drawText(g, Window.sizeX / 2, 320, Game.homedir.replace("\\", "/") + Game.logPath); + Window.drawInterfaceText(g, Window.sizeX / 2, 200, Game.crashMessage); + Window.drawInterfaceText(g, Window.sizeX / 2, 280, "Check the log file for more information: "); + Window.drawInterfaceText(g, Window.sizeX / 2, 320, Game.homedir.replace("\\", "/") + Game.logPath); - Window.drawText(g, Window.sizeX / 2, 400, "You may return to the game if you wish,"); - Window.drawText(g, Window.sizeX / 2, 440, "but be warned that things may become unstable."); - Window.drawText(g, Window.sizeX / 2, 480, "If you see this screen again, restart the game."); - Window.drawText(g, Window.sizeX / 2, 520, "Also, you may want to report this crash!"); + Window.drawInterfaceText(g, Window.sizeX / 2, 400, "You may return to the game if you wish,"); + Window.drawInterfaceText(g, Window.sizeX / 2, 440, "but be warned that things may become unstable."); + Window.drawInterfaceText(g, Window.sizeX / 2, 480, "If you see this screen again, restart the game."); + Window.drawInterfaceText(g, Window.sizeX / 2, 520, "Also, you may want to report this crash!"); - this.quit.draw(g, Window.sizeX / 2, Window.sizeY - 160); - this.exit.draw(g, Window.sizeX / 2, Window.sizeY - 100); + this.quit.draw(g); + this.exit.draw(g); return; } diff --git a/src/tanks/ScreenGame.java b/src/tanks/ScreenGame.java index 8e92b471..c0b77d2c 100755 --- a/src/tanks/ScreenGame.java +++ b/src/tanks/ScreenGame.java @@ -1,13 +1,23 @@ package tanks; +import java.awt.Color; import java.awt.Graphics; import java.awt.event.KeyEvent; +import java.io.File; +import java.util.ArrayList; public class ScreenGame extends Screen { public boolean playing = false; - - Button play = new Button(350, 40, "Play", new Runnable() + public boolean paused = false; + public static boolean finished = false; + public static double finishTimer = 100; + public static double finishTimerMax = 100; + public String name = null; + + public boolean screenshotMode = false; + + Button play = new Button(Window.interfaceSizeX-200, Window.interfaceSizeY-50, 350, 40, "Play", new Runnable() { @Override public void run() @@ -17,18 +27,113 @@ public void run() } } ); + + Button resume = new Button(Window.interfaceSizeX / 2, Window.interfaceSizeY / 2 - 60, 350, 40, "Continue playing", new Runnable() + { + @Override + public void run() + { + paused = false; + Game.player.cooldown = 20; + } + } + ); + + Button newLevel = new Button(Window.interfaceSizeX / 2, Window.interfaceSizeY / 2, 350, 40, "Generate a new level", new Runnable() + { + @Override + public void run() + { + playing = false; + Game.startTime = 400; + paused = false; + Game.reset(); + } + } + ); + + Button edit = new Button(Window.interfaceSizeX / 2, Window.interfaceSizeY / 2, 350, 40, "Edit the level", new Runnable() + { + @Override + public void run() + { + Game.exitToTitle(); + ScreenLevelBuilder s = new ScreenLevelBuilder(name); + Game.loadLevel(new File(Game.homedir + ScreenSavedLevels.levelDir + "/" + name), s); + Game.screen = s; + } + } + ); + + Button quit = new Button(Window.interfaceSizeX / 2, Window.interfaceSizeY / 2 + 60, 350, 40, "Quit to title", new Runnable() + { + @Override + public void run() + { + Game.exitToTitle(); + } + } + ); + + public ScreenGame() + { + Game.startTime = 400; + ScreenGame.finishTimer = ScreenGame.finishTimerMax; + } + public ScreenGame(String s) + { + this(); + this.name = s; + } + @Override public void update() { + if (InputKeyboard.keys.contains(KeyEvent.VK_ESCAPE)) + { + if (!Panel.pausePressed) + { + this.paused = !this.paused; + } + + Panel.pausePressed = true; + } + else + Panel.pausePressed = false; + + if (InputKeyboard.validKeys.contains(KeyEvent.VK_F1)) + { + this.screenshotMode = !this.screenshotMode; + InputKeyboard.validKeys.remove((Integer)KeyEvent.VK_F1); + } + + if (InputKeyboard.validKeys.contains(KeyEvent.VK_I)) + { + Window.movingCamera = !Window.movingCamera ; + InputKeyboard.validKeys.remove((Integer)KeyEvent.VK_I); + } + + if (paused) + { + if (name == null) + newLevel.update(); + else + edit.update(); + + quit.update(); + resume.update(); + return; + } + if (!playing && Game.startTime >= 0) { if (Game.autostart) Game.startTime -= Panel.frameFrequency; - - play.update(Window.sizeX-200, Window.sizeY-50); - if (Game.movables.contains(Game.player)) + play.update(); + + if (!finished) { Obstacle.draw_size = Math.min(Game.tank_size, Obstacle.draw_size + Panel.frameFrequency); } @@ -36,16 +141,24 @@ public void update() else { playing = true; - Game.startTime = 400; - + + //System.out.println(Panel.frameFrequency); + Obstacle.draw_size = Math.min(Obstacle.obstacle_size, Obstacle.draw_size); - int tanks = 0; + ArrayList aliveTeams = new ArrayList(); + for (int i = 0; i < Game.movables.size(); i++) { Movable m = Game.movables.get(i); m.update(); + if (m instanceof Tank) - tanks++; + { + if (m.team == null) + aliveTeams.add(new Team("null")); + else if (!aliveTeams.contains(m.team)) + aliveTeams.add(m.team); + } } for (int i = 0; i < Game.effects.size(); i++) @@ -58,56 +171,55 @@ public void update() Game.belowEffects.get(i).update(); } - if (!Game.movables.contains(Game.player)) + if (aliveTeams.size() <= 1) { - for (int m = 0; m < Game.movables.size(); m++) + ScreenGame.finished = true; + Game.bulletLocked = true; + + if (ScreenGame.finishTimer > 0) { - Movable mo = Game.movables.get(m); - if (mo instanceof Bullet || mo instanceof Mine) - mo.destroy = true; + ScreenGame.finishTimer -= Panel.frameFrequency; + if (ScreenGame.finishTimer < 0) + ScreenGame.finishTimer = 0; } - - if (Game.effects.size() == 0) + else { - Obstacle.draw_size = Math.max(0, Obstacle.draw_size - Panel.frameFrequency); - for (int i = 0; i < Game.movables.size(); i++) - Game.movables.get(i).destroy = true; - - if (Obstacle.draw_size <= 0) + boolean noMovables = true; + + for (int m = 0; m < Game.movables.size(); m++) { - Panel.winlose = "You were destroyed!"; - Panel.win = false; - Game.exit(); + Movable mo = Game.movables.get(m); + if (mo instanceof Bullet || mo instanceof Mine) + { + noMovables = false; + mo.destroy = true; + } } - } - - } - - /*for (int i = 0; i < Game.obstacles.size(); i++) - { - Game.obstacles.get(i).posX += (Game.obstacles.get(i).posX - Game.player.posX) / 1000; - Game.obstacles.get(i).posY += (Game.obstacles.get(i).posY - Game.player.posY) / 1000; - }*/ - - if (tanks <= 1 && !Game.player.destroy) - { - Game.bulletLocked = true; - for (int m = 0; m < Game.movables.size(); m++) - { - Movable mo = Game.movables.get(m); - if (mo instanceof Bullet || mo instanceof Mine) - mo.destroy = true; - } - - if (Game.effects.size() == 0) - { - Obstacle.draw_size = Math.max(0, Obstacle.draw_size - Panel.frameFrequency); - if (Obstacle.draw_size <= 0) + if (Game.effects.size() <= 0 && noMovables) { - Panel.winlose = "Level Cleared!"; - Panel.win = true; - Game.exit(); + Obstacle.draw_size = Math.max(0, Obstacle.draw_size - Panel.frameFrequency); + for (int i = 0; i < Game.movables.size(); i++) + Game.movables.get(i).destroy = true; + + if (Obstacle.draw_size <= 0) + { + if (aliveTeams.contains(Game.player.team)) + { + Panel.winlose = "Victory!"; + Panel.win = true; + } + else + { + Panel.winlose = "You were destroyed!"; + Panel.win = false; + } + + if (name != null) + Game.exit(name); + else + Game.exit(); + } } } } @@ -122,30 +234,25 @@ public void update() Game.obstacles.remove(Game.removeObstacles.get(i)); for (int i = 0; i < Game.removeEffects.size(); i++) - Game.effects.remove(Game.removeEffects.get(i)); + { + Effect e = Game.removeEffects.get(i); + Game.effects.remove(e); + Game.recycleEffects.add(e); + + } for (int i = 0; i < Game.removeBelowEffects.size(); i++) - Game.belowEffects.remove(Game.removeBelowEffects.get(i)); + { + Effect e = Game.removeBelowEffects.get(i); + Game.belowEffects.remove(e); + Game.recycleEffects.add(e); + } Game.removeMovables.clear(); Game.removeObstacles.clear(); Game.removeEffects.clear(); Game.removeBelowEffects.clear(); - if (KeyInputListener.keys.contains(KeyEvent.VK_ESCAPE)) - { - if (!Panel.pausePressed) - { - ScreenPaused scr = new ScreenPaused(); - scr.playing = this.playing; - Game.screen = scr; - } - - Panel.pausePressed = true; - } - else - Panel.pausePressed = false; - } @Override @@ -158,15 +265,31 @@ public void draw(Graphics g) for (int n = 0; n < Game.movables.size(); n++) Game.movables.get(n).draw(g); - + for (int i = 0; i < Game.obstacles.size(); i++) Game.obstacles.get(i).draw(g); for (int i = 0; i < Game.effects.size(); i++) ((Effect)Game.effects.get(i)).draw(g); - + if (!playing) - play.draw(g, Window.sizeX-200, Window.sizeY-50); + play.draw(g); + + if (paused && !screenshotMode) + { + g.setColor(new Color(127, 178, 228, 64)); + g.fillRect(0, 0, (int) (Game.window.getSize().getWidth()) + 1, (int) (Game.window.getSize().getHeight()) + 1); + + if (name == null) + newLevel.draw(g); + else + edit.draw(g); + + quit.draw(g); + resume.draw(g); + g.setColor(Color.black); + Window.drawInterfaceText(g, Window.interfaceSizeX / 2, Window.interfaceSizeY / 2 - 150, "Game paused"); + } } diff --git a/src/tanks/ScreenInterlevel.java b/src/tanks/ScreenInterlevel.java index 1c4d0143..f221dafb 100755 --- a/src/tanks/ScreenInterlevel.java +++ b/src/tanks/ScreenInterlevel.java @@ -9,7 +9,7 @@ public class ScreenInterlevel extends Screen ArrayList fireworks = new ArrayList(); ArrayList removeFireworks = new ArrayList(); - Button replay = new Button(350, 40, "Replay the level", new Runnable() + Button replay = new Button(Window.interfaceSizeX / 2, Window.interfaceSizeY / 2 - 30, 350, 40, "Replay the level", new Runnable() { @Override public void run() @@ -21,7 +21,20 @@ public void run() } ); - Button newLevel = new Button(350, 40, "Generate a new level", new Runnable() + Button save = new Button(Window.interfaceSizeX / 2, Window.interfaceSizeY / 2 + 30, 350, 40, "Save this level", new Runnable() + { + @Override + public void run() + { + ScreenLevelBuilder s = new ScreenLevelBuilder(System.currentTimeMillis() + ".tanks", false); + Level level = new Level(Game.currentLevel); + level.loadLevel(s); + Game.screen = s; + } + } + ); + + Button newLevel = new Button(Window.interfaceSizeX / 2, Window.interfaceSizeY / 2 - 90, 350, 40, "Generate a new level", new Runnable() { @Override public void run() @@ -32,7 +45,7 @@ public void run() } ); - Button quit = new Button(350, 40, "Quit to title", new Runnable() + Button quit = new Button(Window.interfaceSizeX / 2, Window.interfaceSizeY / 2 + 90, 350, 40, "Quit to title", new Runnable() { @Override public void run() @@ -45,9 +58,29 @@ public void run() @Override public void update() { - newLevel.update(Window.sizeX / 2, Window.sizeY / 2 - 60); - replay.update(Window.sizeX / 2, Window.sizeY / 2); - quit.update(Window.sizeX / 2, Window.sizeY / 2 + 60); + newLevel.update(); + replay.update(); + save.update(); + quit.update(); + } + + public ScreenInterlevel() + { + if (Panel.win) + { + Window.playSound("resources/win.wav"); + for (int i = 0; i < 5; i++) + { + Firework f = new Firework(Firework.FireworkType.rocket, (Math.random() * 0.6 + 0.2) * Window.sizeX, Window.sizeY, fireworks, removeFireworks); + f.setRandomColor(); + f.vY = - Math.random() * 3 - 6; + f.vX = Math.random() * 5 - 2.5; + fireworks.add(f); + } + } + else + Window.playSound("resources/lose.wav"); + } @Override @@ -77,13 +110,14 @@ public void draw(Graphics g) } } - newLevel.draw(g, Window.sizeX / 2, Window.sizeY / 2 - 60); - replay.draw(g, Window.sizeX / 2, Window.sizeY / 2); - quit.draw(g, Window.sizeX / 2, Window.sizeY / 2 + 60); + newLevel.draw(g); + replay.draw(g); + save.draw(g); + quit.draw(g); if (Panel.win && Game.graphicalEffects) g.setColor(Color.white); - Window.drawText(g, Window.sizeX / 2, Window.sizeY / 2 - 150, Panel.winlose); + Window.drawInterfaceText(g, Window.interfaceSizeX / 2, Window.interfaceSizeY / 2 - 150, Panel.winlose); if (Panel.win && Game.graphicalEffects) Panel.darkness = Math.min(Panel.darkness + Panel.frameFrequency * 1.5, 191); diff --git a/src/tanks/ScreenLevelBuilder.java b/src/tanks/ScreenLevelBuilder.java index 4603d452..01535550 100755 --- a/src/tanks/ScreenLevelBuilder.java +++ b/src/tanks/ScreenLevelBuilder.java @@ -7,6 +7,7 @@ import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; +import java.util.ArrayList; public class ScreenLevelBuilder extends Screen { @@ -14,43 +15,778 @@ enum Placeable {enemyTank, playerTank, obstacle} Placeable currentPlaceable = Placeable.enemyTank; int tankNum = 0; + int teamNum = 1; + int playerTeamNum = 0; + + boolean reloadNewLevel = true; + Tank mouseTank = Game.registry.getRegistry(tankNum).getTank(0, 0, 0); Obstacle mouseObstacle = new Obstacle(0, 0, Obstacle.getRandomColor()); + boolean paused = true; + boolean optionsMenu = false; + boolean sizeMenu = false; + boolean colorMenu = false; + boolean teamsMenu = false; + boolean editTeamMenu = false; + boolean teamColorMenu = false; + Team selectedTeam; + + int rows = 6; + int page = 0; + + public boolean editable = true; + + ArrayList teams = new ArrayList(); + ArrayList