From 595307dee5b53b57ffa287be168ec361ac9adc54 Mon Sep 17 00:00:00 2001 From: Aehmttw Date: Sat, 26 Mar 2022 15:31:05 -0400 Subject: [PATCH] Tanks v1.3.2 - bug fixes --- src/main/java/basewindow/ModelPart.java | 2 +- src/main/java/tanks/CrusadePlayer.java | 10 +- src/main/java/tanks/Effect.java | 8 +- src/main/java/tanks/Game.java | 4 +- src/main/java/tanks/Panel.java | 5 +- src/main/java/tanks/bullet/Bullet.java | 3 +- src/main/java/tanks/bullet/BulletArc.java | 36 +++- .../java/tanks/bullet/BulletElectric.java | 50 +++++ .../java/tanks/bullet/BulletExplosive.java | 7 +- src/main/java/tanks/bullet/Laser.java | 5 + src/main/java/tanks/event/EventExplosion.java | 68 +++++++ src/main/java/tanks/event/EventLayMine.java | 12 +- .../java/tanks/event/EventMineExplode.java | 3 +- .../tanks/gui/screen/ScreenChangelog.java | 8 + .../java/tanks/network/ClientHandler.java | 2 +- .../java/tanks/network/ServerHandler.java | 2 +- .../tanks/network/SteamNetworkHandler.java | 5 +- .../tanks/obstacle/ObstacleExplosive.java | 23 +-- src/main/java/tanks/tank/Explosion.java | 179 ++++++++++++++++++ src/main/java/tanks/tank/Mine.java | 140 +------------- src/main/java/tanks/tank/Tank.java | 10 +- .../java/tanks/tank/TankAIControlled.java | 8 +- src/main/java/tanks/tank/TankGold.java | 6 +- src/main/java/tanks/tank/TankGreen.java | 2 +- src/main/java/tanks/tank/TankMedic.java | 5 +- src/main/java/tanks/tank/TankMustard.java | 4 +- src/main/java/tanks/tank/TankOrangeRed.java | 8 +- src/main/java/tanks/tank/TankPink.java | 2 +- src/main/java/tanks/tank/TankTest.java | 37 ++++ .../crusades/adventure_crusade.tanks | 2 +- src/main/resources/items/items.tanks | 4 +- 31 files changed, 443 insertions(+), 217 deletions(-) create mode 100644 src/main/java/tanks/event/EventExplosion.java create mode 100644 src/main/java/tanks/tank/Explosion.java create mode 100755 src/main/java/tanks/tank/TankTest.java diff --git a/src/main/java/basewindow/ModelPart.java b/src/main/java/basewindow/ModelPart.java index 2de4886f..5c0f140b 100755 --- a/src/main/java/basewindow/ModelPart.java +++ b/src/main/java/basewindow/ModelPart.java @@ -78,7 +78,7 @@ public abstract static class ShapeDrawer public abstract void drawShape(ModelPart m, Shape s, double posX, double posY, double sX, double sY, double yaw); } - @Deprecated + //@Deprecated public static class Quad extends Shape { public Quad(Point a, Point b, Point c, Point d, double brightness) diff --git a/src/main/java/tanks/CrusadePlayer.java b/src/main/java/tanks/CrusadePlayer.java index 7c13d1fb..5fab908a 100755 --- a/src/main/java/tanks/CrusadePlayer.java +++ b/src/main/java/tanks/CrusadePlayer.java @@ -92,10 +92,10 @@ public int getItemHits(String i) public void addItemStat(HashMap stat, IGameObject i) { - tanks.hotbar.item.Item item; + Item item; - if (i instanceof tanks.hotbar.item.Item) - item = (tanks.hotbar.item.Item) i; + if (i instanceof Item) + item = (Item) i; else if (i instanceof Bullet) { item = ((Bullet) i).item; @@ -103,9 +103,9 @@ else if (i instanceof Bullet) if (item == null) item = TankPlayer.default_bullet; } - else if (i instanceof Mine) + else if (i instanceof Explosion) { - item = ((Mine) i).item; + item = ((Explosion) i).item; if (item == null) item = TankPlayer.default_mine; diff --git a/src/main/java/tanks/Effect.java b/src/main/java/tanks/Effect.java index aa0e88da..b62ffc09 100755 --- a/src/main/java/tanks/Effect.java +++ b/src/main/java/tanks/Effect.java @@ -6,7 +6,7 @@ public class Effect extends Movable implements IDrawableWithGlow { - public enum EffectType {fire, smokeTrail, trail, ray, mineExplosion, laser, piece, obstaclePiece, obstaclePiece3d, charge, tread, darkFire, electric, healing, stun, bushBurn, glow, teleporterLight, teleporterPiece, interfacePiece, snow, shield, boostLight, exclamation} + public enum EffectType {fire, smokeTrail, trail, ray, explosion, laser, piece, obstaclePiece, obstaclePiece3d, charge, tread, darkFire, electric, healing, stun, bushBurn, glow, teleporterLight, teleporterPiece, interfacePiece, snow, shield, boostLight, exclamation} public enum State {live, removed, recycle} @@ -94,7 +94,7 @@ else if (type == EffectType.trail) this.maxAge = 50; else if (type == EffectType.ray) this.maxAge = 20; - else if (type == EffectType.mineExplosion) + else if (type == EffectType.explosion) { this.maxAge = 20; this.force = true; @@ -113,7 +113,7 @@ else if (type == EffectType.obstaclePiece3d) else if (type.equals(EffectType.charge)) { if (Game.enable3d) - this.add3dPolarMotion(Math.random() * Math.PI * 2,-Math.random() * Math.PI / 2, Math.random() * 3 + 3); + this.add3dPolarMotion(Math.random() * Math.PI * 2, -Math.atan(Math.random()), Math.random() * 3 + 3); else this.addPolarMotion(Math.random() * Math.PI * 2, Math.random() * 3 + 3); @@ -271,7 +271,7 @@ else if (this.type == EffectType.ray) else drawing.fillOval(this.posX, this.posY, size, size); } - else if (this.type == EffectType.mineExplosion) + else if (this.type == EffectType.explosion) { double size = (radius * 2); double opacity = (100 - this.age * 5); diff --git a/src/main/java/tanks/Game.java b/src/main/java/tanks/Game.java index ba8946b1..23835abd 100755 --- a/src/main/java/tanks/Game.java +++ b/src/main/java/tanks/Game.java @@ -92,7 +92,7 @@ public enum Framework {lwjgl, libgdx} public static double[][] tilesDepth = new double[28][18]; //Remember to change the version in android's build.gradle and ios's robovm.properties - public static final String version = "Tanks v1.3.1"; + public static final String version = "Tanks v1.3.2"; public static final int network_protocol = 39; public static boolean debug = false; public static boolean traceAllRays = false; @@ -296,6 +296,7 @@ public static void registerEvents() NetworkEventMap.register(EventLayMine.class); NetworkEventMap.register(EventMineExplode.class); NetworkEventMap.register(EventMineChangeTimer.class); + NetworkEventMap.register(EventExplosion.class); NetworkEventMap.register(EventTankTeleport.class); NetworkEventMap.register(EventTankUpdateVisibility.class); NetworkEventMap.register(EventTankUpdateColor.class); @@ -440,6 +441,7 @@ public static void initScript() registerTank(TankPink.class, "pink", 1.0 / 12); registerTank(TankMini.class, "mini", 0); registerTank(TankLightPink.class, "lightpink", 1.0 / 10); + //registerTank(TankTest.class, "test", 1.0 / 1); registerTank(TankBoss.class, "boss", 1.0 / 40, true); registerBullet(Bullet.class, Bullet.bullet_name, "bullet_normal.png"); diff --git a/src/main/java/tanks/Panel.java b/src/main/java/tanks/Panel.java index b028f43e..6b37cd3b 100755 --- a/src/main/java/tanks/Panel.java +++ b/src/main/java/tanks/Panel.java @@ -514,7 +514,10 @@ else if (((ScreenGame) Game.screen).spectatingTank instanceof TankPlayerRemote) else speed = 0.02; - this.zoomTimer = Math.max(this.zoomTimer + speed * Panel.frameFrequency, Panel.zoomTarget); + if (this.zoomTimer > Panel.zoomTarget) + this.zoomTimer = Math.max(this.zoomTimer + speed * Panel.frameFrequency, Panel.zoomTarget); + else + this.zoomTimer = Math.min(this.zoomTimer + speed * Panel.frameFrequency, Panel.zoomTarget); } } diff --git a/src/main/java/tanks/bullet/Bullet.java b/src/main/java/tanks/bullet/Bullet.java index ca545ea8..2d62b7e8 100755 --- a/src/main/java/tanks/bullet/Bullet.java +++ b/src/main/java/tanks/bullet/Bullet.java @@ -654,7 +654,8 @@ public void update() this.item.liveBullets--; } - this.onDestroy(); + if (!this.isRemote) + this.onDestroy(); } if (this.destroyTimer <= 0 && Game.effectsEnabled && !(this instanceof BulletFlame)) diff --git a/src/main/java/tanks/bullet/BulletArc.java b/src/main/java/tanks/bullet/BulletArc.java index 8376a8f4..b6274896 100755 --- a/src/main/java/tanks/bullet/BulletArc.java +++ b/src/main/java/tanks/bullet/BulletArc.java @@ -83,19 +83,35 @@ public void update() if (this.posZ <= Game.tile_size / 2 && !this.destroy) { - double dif = (this.posZ - Game.tile_size / 2) / this.vZ; - this.posX -= dif * this.vX; - this.posY -= dif * this.vY; + if (this.bounces > 0) + { + this.bounces--; + this.posZ += 2 * ((Game.tile_size / 2) - this.posZ); + this.vZ = Math.abs(this.vZ) * 0.75; + + if (!this.tank.isRemote) + this.checkCollision(); - this.vX = 0; - this.vY = 0; - this.vZ = 0; + this.checkCollisionLocal(); + } + else + { + double dif = (this.posZ - Game.tile_size / 2) / this.vZ; + this.posX -= dif * this.vX; + this.posY -= dif * this.vY; - if (!this.tank.isRemote) - this.checkCollision(); + this.vX = 0; + this.vY = 0; + this.vZ = 0; + + if (!this.tank.isRemote) + this.checkCollision(); + + this.checkCollisionLocal(); + + this.destroy = true; + } - this.checkCollisionLocal(); - this.destroy = true; Drawing.drawing.playSound("bullet_explode.ogg", (float) (Bullet.bullet_size / this.size)); } diff --git a/src/main/java/tanks/bullet/BulletElectric.java b/src/main/java/tanks/bullet/BulletElectric.java index 90dbf206..76128f96 100755 --- a/src/main/java/tanks/bullet/BulletElectric.java +++ b/src/main/java/tanks/bullet/BulletElectric.java @@ -259,4 +259,54 @@ public void addDestroyEffect() } } } + + @Override + public void collided() + { + double dist = Math.sqrt(Math.pow(this.collisionX - this.lastX, 2) + Math.pow(this.collisionY - this.lastY, 2)); + + double r = 200; + boolean glows = false; + double size = 0.25; + + if (Game.fancyBulletTrails) + { + for (int j = 0; j < 2; j++) + { + int segs = (int) ((Math.random() * 0.4 + 0.8) * dist / 50); + + double lX = this.lastX; + double lY = this.lastY; + double lZ = this.lastZ; + + for (int i = 0; i < segs; i++) + { + double frac = (i + 1.0) / (segs + 1); + double nX = (1 - frac) * this.lastX + frac * this.collisionX + (Math.random() - 0.5) * 50; + double nY = (1 - frac) * this.lastY + frac * this.collisionY + (Math.random() - 0.5) * 50; + double nZ = (1 - frac) * this.lastZ + frac * this.posZ + (Math.random() - 0.5) * 30; + Laser l = new Laser(lX, lY, lZ, nX, nY, nZ, this.size * size, this.getAngleInDirection(this.lastX, this.lastY), r, 255, 255); + l.glows = glows; + this.segments.add(l); + lX = nX; + lY = nY; + lZ = nZ; + } + Laser l = new Laser(lX, lY, lZ, this.collisionX, this.collisionY, this.posZ, this.size * size, this.getAngleInDirection(this.lastX, this.lastY), r, 255, 255); + l.glows = glows; + this.segments.add(l); + } + } + + this.segments.add(new Laser(this.lastX, this.lastY, this.lastZ, this.collisionX, this.collisionY, this.posZ, this.size / 2, this.getAngleInDirection(this.lastX, this.lastY), this.baseColorR, this.baseColorG, this.baseColorB)); + this.lastX = this.collisionX; + this.lastY = this.collisionY; + this.lastZ = this.posZ; + + if (!this.isRemote) + { + this.xTargets.add(this.collisionX); + this.yTargets.add(this.collisionY); + } + } } diff --git a/src/main/java/tanks/bullet/BulletExplosive.java b/src/main/java/tanks/bullet/BulletExplosive.java index 9368134f..0e2b9c2e 100755 --- a/src/main/java/tanks/bullet/BulletExplosive.java +++ b/src/main/java/tanks/bullet/BulletExplosive.java @@ -3,6 +3,7 @@ import tanks.Game; import tanks.event.EventLayMine; import tanks.hotbar.item.ItemBullet; +import tanks.tank.Explosion; import tanks.tank.Mine; import tanks.tank.Tank; @@ -36,10 +37,8 @@ public BulletExplosive(Double x, Double y, Integer bounces, Tank t, ItemBullet i @Override public void onDestroy() { - Mine m = new Mine(this.posX, this.posY, 0, this.tank); - m.item = this.item; - Game.eventsOut.add(new EventLayMine(m)); - Game.movables.add(m); + Explosion e = new Explosion(this.posX, this.posY, Mine.mine_radius, this.damage, true, this.tank, this.item); + e.explode(); } @Override diff --git a/src/main/java/tanks/bullet/Laser.java b/src/main/java/tanks/bullet/Laser.java index 7e91cfdf..2636a44a 100755 --- a/src/main/java/tanks/bullet/Laser.java +++ b/src/main/java/tanks/bullet/Laser.java @@ -30,6 +30,8 @@ public class Laser extends Movable implements IDrawableWithGlow public boolean backCircle = true; public boolean showOutsides = true; + public boolean glows = true; + public Tank tank1; public Tank tank2; @@ -118,6 +120,9 @@ public void draw() public void drawGlow() { + if (!glows) + return; + double ox = Math.cos(this.angle + Math.PI / 2); double oy = Math.sin(this.angle + Math.PI / 2); diff --git a/src/main/java/tanks/event/EventExplosion.java b/src/main/java/tanks/event/EventExplosion.java new file mode 100644 index 00000000..9f50a9ed --- /dev/null +++ b/src/main/java/tanks/event/EventExplosion.java @@ -0,0 +1,68 @@ +package tanks.event; + +import io.netty.buffer.ByteBuf; +import tanks.Game; +import tanks.tank.Explosion; +import tanks.tank.Mine; +import tanks.tank.Tank; + +public class EventExplosion extends PersonalEvent +{ + public int tank; + public double posX; + public double posY; + public double radius; + public boolean destroysObstacles; + + public EventExplosion() + { + + } + + public EventExplosion(Explosion e) + { + this.tank = e.tank.networkID; + this.posX = e.posX; + this.posY = e.posY; + this.radius = e.radius; + this.destroysObstacles = e.destroysObstacles; + } + + @Override + public void execute() + { + if (clientID == null) + { + Tank t = Tank.idMap.get(tank); + + if (tank == -1) + t = Game.dummyTank; + + if (t == null) + return; + + Explosion e = new Explosion(this.posX, this.posY, this.radius, 0, destroysObstacles, t); + e.explode(); + } + } + + @Override + public void write(ByteBuf b) + { + b.writeInt(this.tank); + b.writeDouble(this.posX); + b.writeDouble(this.posY); + b.writeDouble(this.radius); + b.writeBoolean(this.destroysObstacles); + } + + @Override + public void read(ByteBuf b) + { + this.tank = b.readInt(); + this.posX = b.readDouble(); + this.posY = b.readDouble(); + this.radius = b.readDouble(); + this.destroysObstacles = b.readBoolean(); + } +} diff --git a/src/main/java/tanks/event/EventLayMine.java b/src/main/java/tanks/event/EventLayMine.java index 69b5d607..7b14c104 100755 --- a/src/main/java/tanks/event/EventLayMine.java +++ b/src/main/java/tanks/event/EventLayMine.java @@ -13,10 +13,8 @@ public class EventLayMine extends PersonalEvent public double posX; public double posY; public double timer; - public double radius; public double size; - public boolean destroysObstacles; - + public EventLayMine() { @@ -29,9 +27,7 @@ public EventLayMine(Mine m) this.posX = m.posX; this.posY = m.posY; this.timer = m.timer; - this.radius = m.radius; this.size = m.size; - this.destroysObstacles = m.destroysObstacles; } @Override @@ -50,8 +46,6 @@ public void execute() Mine m = new Mine(this.posX, this.posY, this.timer, t); m.networkID = id; m.size = size; - m.radius = radius; - m.destroysObstacles = destroysObstacles; Game.movables.add(m); Mine.idMap.put(id, m); @@ -66,9 +60,7 @@ public void write(ByteBuf b) b.writeDouble(this.posX); b.writeDouble(this.posY); b.writeDouble(this.timer); - b.writeDouble(this.radius); b.writeDouble(this.size); - b.writeBoolean(this.destroysObstacles); } @Override @@ -79,8 +71,6 @@ public void read(ByteBuf b) this.posX = b.readDouble(); this.posY = b.readDouble(); this.timer = b.readDouble(); - this.radius = b.readDouble(); this.size = b.readDouble(); - this.destroysObstacles = b.readBoolean(); } } diff --git a/src/main/java/tanks/event/EventMineExplode.java b/src/main/java/tanks/event/EventMineExplode.java index 11b059da..fda8520b 100755 --- a/src/main/java/tanks/event/EventMineExplode.java +++ b/src/main/java/tanks/event/EventMineExplode.java @@ -1,6 +1,7 @@ package tanks.event; import io.netty.buffer.ByteBuf; +import tanks.Game; import tanks.tank.Mine; public class EventMineExplode extends PersonalEvent @@ -28,7 +29,7 @@ public void execute() if (m == null) return; - m.explode(); + Game.removeMovables.add(m); if (!Mine.freeIDs.contains(m.networkID)) { diff --git a/src/main/java/tanks/gui/screen/ScreenChangelog.java b/src/main/java/tanks/gui/screen/ScreenChangelog.java index 0337d818..cec615da 100755 --- a/src/main/java/tanks/gui/screen/ScreenChangelog.java +++ b/src/main/java/tanks/gui/screen/ScreenChangelog.java @@ -360,6 +360,14 @@ public static void setupLogs() "Improved keybindings screen\n" + "Bug fixes and other minor improvements\n" }); + + new Changelog("v1.3.2", new String[] + { + "*What's new in Tanks v1.3.2:\n\n" + + "Changed appearance of electric bullets\n" + + "Arc bullets can now be set to bounce\n" + + "Bug fixes and other minor improvements\n" + }); } } } diff --git a/src/main/java/tanks/network/ClientHandler.java b/src/main/java/tanks/network/ClientHandler.java index 01b64616..eb125a4d 100755 --- a/src/main/java/tanks/network/ClientHandler.java +++ b/src/main/java/tanks/network/ClientHandler.java @@ -216,7 +216,7 @@ public void reply() for (int i = 0; i < Game.eventsOut.size(); i++) { INetworkEvent e = Game.eventsOut.get(i); - this.sendEvent(e, false); + this.sendEvent(e, i >= Game.eventsOut.size() - 1); } if (steamID == null) diff --git a/src/main/java/tanks/network/ServerHandler.java b/src/main/java/tanks/network/ServerHandler.java index 0c78ea0e..df2a06de 100755 --- a/src/main/java/tanks/network/ServerHandler.java +++ b/src/main/java/tanks/network/ServerHandler.java @@ -129,7 +129,7 @@ public void reply() for (int i = 0; i < this.events.size(); i++) { INetworkEvent e = this.events.get(i); - this.sendEvent(e, false); + this.sendEvent(e, i >= Game.eventsOut.size() - 1); } if (steamID == null) diff --git a/src/main/java/tanks/network/SteamNetworkHandler.java b/src/main/java/tanks/network/SteamNetworkHandler.java index 471bcc7d..0d0a1a7c 100755 --- a/src/main/java/tanks/network/SteamNetworkHandler.java +++ b/src/main/java/tanks/network/SteamNetworkHandler.java @@ -324,10 +324,9 @@ public boolean load() } catch (Throwable e) { - Game.exitToCrash(e); + e.printStackTrace(); + return false; } - - return false; } public void exit() diff --git a/src/main/java/tanks/obstacle/ObstacleExplosive.java b/src/main/java/tanks/obstacle/ObstacleExplosive.java index b3d1bf5b..d25045cc 100755 --- a/src/main/java/tanks/obstacle/ObstacleExplosive.java +++ b/src/main/java/tanks/obstacle/ObstacleExplosive.java @@ -9,10 +9,7 @@ import tanks.gui.Button; import tanks.gui.screen.ScreenPartyLobby; import tanks.hotbar.item.Item; -import tanks.tank.IAvoidObject; -import tanks.tank.Mine; -import tanks.tank.Tank; -import tanks.tank.TankPlayer; +import tanks.tank.*; public class ObstacleExplosive extends Obstacle implements IAvoidObject { @@ -81,12 +78,12 @@ public void onDestroy(Movable m) if (!ScreenPartyLobby.isClient) this.update = true; - if (m instanceof Mine) + if (m instanceof Explosion) { - this.trigger = ((Mine) m).tank; - this.itemTrigger = ((Mine) m).item; + this.trigger = ((Explosion) m).tank; + this.itemTrigger = ((Explosion) m).item; - if (((Mine) m).item == null) + if (((Explosion) m).item == null) this.itemTrigger = TankPlayer.default_mine; } } @@ -105,14 +102,8 @@ public void explode() if (ScreenPartyLobby.isClient) return; - Mine mi = new Mine(this.posX, this.posY, 0, this.trigger); - mi.item = this.itemTrigger; - mi.radius *= (this.stackHeight - 1) / 2 + 1; - Game.eventsOut.add(new EventLayMine(mi)); - Game.movables.add(mi); - - //if (this.trigger != null) - // this.trigger.liveMines--; + Explosion e = new Explosion(this.posX, this.posY, Mine.mine_radius * ((this.stackHeight - 1) / 2 + 1), 2, true, this.trigger, this.itemTrigger); + e.explode(); Game.removeObstacles.add(this); Game.eventsOut.add(new EventObstacleDestroy(this.posX, this.posY)); diff --git a/src/main/java/tanks/tank/Explosion.java b/src/main/java/tanks/tank/Explosion.java new file mode 100644 index 00000000..791b5b7a --- /dev/null +++ b/src/main/java/tanks/tank/Explosion.java @@ -0,0 +1,179 @@ +package tanks.tank; + +import tanks.*; +import tanks.bullet.Bullet; +import tanks.event.EventChat; +import tanks.event.EventExplosion; +import tanks.event.EventMineChangeTimer; +import tanks.event.EventUpdateCoins; +import tanks.gui.ChatMessage; +import tanks.gui.IFixedMenu; +import tanks.gui.Scoreboard; +import tanks.gui.screen.ScreenGame; +import tanks.gui.screen.ScreenPartyHost; +import tanks.gui.screen.ScreenPartyLobby; +import tanks.hotbar.item.Item; +import tanks.obstacle.Obstacle; + +public class Explosion extends Movable +{ + public double damage; + public boolean destroysObstacles; + + public double radius; + public Tank tank; + public Item item; + + public Explosion(double x, double y, double radius, double damage, boolean destroysObstacles, Tank tank, Item item) + { + super(x, y); + + this.tank = tank; + this.item = item; + this.radius = radius; + this.damage = damage; + this.destroysObstacles = destroysObstacles; + this.team = tank.team; + this.isRemote = tank.isRemote; + } + + public Explosion(double x, double y, double radius, double damage, boolean destroysObstacles, Tank tank) + { + this(x, y, radius, damage, destroysObstacles, tank, null); + } + + public Explosion(Mine m) + { + this(m.posX, m.posY, m.radius, m.damage, m.destroysObstacles, m.tank, m.item); + } + + public void explode() + { + Drawing.drawing.playSound("explosion.ogg", (float) (Mine.mine_radius / this.radius)); + + if (Game.effectsEnabled) + { + for (int j = 0; j < 200 * this.radius / 125 * Game.effectMultiplier; j++) + { + double random = Math.random(); + Effect e = Effect.createNewEffect(this.posX, this.posY, Effect.EffectType.piece); + e.maxAge /= 2; + e.colR = 255; + e.colG = (1 - random) * 155 + Math.random() * 100; + e.colB = 0; + + if (Game.enable3d) + e.set3dPolarMotion(Math.random() * 2 * Math.PI, Math.asin(Math.random()), random * (this.radius - Game.tile_size / 2) / Game.tile_size * 2); + else + e.setPolarMotion(Math.random() * 2 * Math.PI, random * (this.radius - Game.tile_size / 2) / Game.tile_size * 2); + Game.effects.add(e); + } + } + + this.destroy = true; + + if (!ScreenPartyLobby.isClient) + { + Game.eventsOut.add(new EventExplosion(this)); + + for (Movable m: Game.movables) + { + if (Math.pow(Math.abs(m.posX - this.posX), 2) + Math.pow(Math.abs(m.posY - this.posY), 2) < Math.pow(radius, 2)) + { + if (m instanceof Tank && !m.destroy && ((Tank) m).getDamageMultiplier(this) > 0) + { + if (!(Team.isAllied(this, m) && !this.team.friendlyFire) && !ScreenGame.finishedQuick) + { + Tank t = (Tank) m; + boolean kill = t.damage(this.damage, this); + + if (kill) + { + if (Game.currentLevel instanceof ModLevel) + { + for (IFixedMenu menu : ModAPI.menuGroup) + { + if (menu instanceof Scoreboard && ((Scoreboard) menu).objectiveType.equals(Scoreboard.objectiveTypes.kills)) + { + if (!((Scoreboard) menu).teams.isEmpty()) + ((Scoreboard) menu).addTeamScore(this.tank.team, 1); + + else if (this.tank instanceof TankPlayer && !((Scoreboard) menu).players.isEmpty()) + ((Scoreboard) menu).addPlayerScore(((TankPlayer) this.tank).player, 1); + + else if (this.tank instanceof TankPlayerRemote && !((Scoreboard) menu).players.isEmpty()) + ((Scoreboard) menu).addPlayerScore(((TankPlayerRemote) this.tank).player, 1); + } + } + + if (((ModLevel) Game.currentLevel).enableKillMessages && ScreenPartyHost.isServer) + { + String message = ((ModLevel) Game.currentLevel).generateKillMessage(t, this.tank, false); + ScreenPartyHost.chat.add(0, new ChatMessage(message)); + Game.eventsOut.add(new EventChat(message)); + } + } + + + if (this.tank.equals(Game.playerTank)) + { + if (Game.currentLevel instanceof ModLevel && (t instanceof TankPlayer || t instanceof TankPlayerRemote)) + Game.player.hotbar.coins += ((ModLevel) Game.currentLevel).playerKillCoins; + else + Game.player.hotbar.coins += t.coinValue; + } + else if (this.tank instanceof TankPlayerRemote && (Crusade.crusadeMode || Game.currentLevel.shop.size() > 0 || Game.currentLevel.startingItems.size() > 0)) + { + if (t instanceof TankPlayer || t instanceof TankPlayerRemote) + { + if (Game.currentLevel instanceof ModLevel && ((ModLevel) Game.currentLevel).playerKillCoins > 0) + ((TankPlayerRemote) this.tank).player.hotbar.coins += ((ModLevel) Game.currentLevel).playerKillCoins; + else + ((TankPlayerRemote) this.tank).player.hotbar.coins += t.coinValue; + } + Game.eventsOut.add(new EventUpdateCoins(((TankPlayerRemote) this.tank).player)); + } + } + else + Drawing.drawing.playGlobalSound("damage.ogg"); + } + } + else if (m instanceof Mine && !m.destroy) + { + if (((Mine) m).timer > 10 && !this.isRemote) + { + ((Mine) m).timer = 10; + Game.eventsOut.add(new EventMineChangeTimer((Mine) m)); + } + } + else if (m instanceof Bullet && !m.destroy) + { + m.destroy = true; + } + } + } + } + + if (this.destroysObstacles) + { + for (Obstacle o: Game.obstacles) + { + if (Math.pow(Math.abs(o.posX - this.posX), 2) + Math.pow(Math.abs(o.posY - this.posY), 2) < Math.pow(radius, 2) && o.destructible && !Game.removeObstacles.contains(o)) + { + o.onDestroy(this); + o.playDestroyAnimation(this.posX, this.posY, this.radius); + } + } + } + + Effect e = Effect.createNewEffect(this.posX, this.posY, Effect.EffectType.explosion); + e.radius = Math.max(this.radius - Game.tile_size * 0.5, 0); + Game.effects.add(e); + } + + @Override + public void draw() + { + + } +} diff --git a/src/main/java/tanks/tank/Mine.java b/src/main/java/tanks/tank/Mine.java index fb572b3e..509f0112 100755 --- a/src/main/java/tanks/tank/Mine.java +++ b/src/main/java/tanks/tank/Mine.java @@ -1,20 +1,13 @@ package tanks.tank; import tanks.*; -import tanks.bullet.Bullet; -import tanks.event.EventChat; import tanks.event.EventMineChangeTimer; import tanks.event.EventMineExplode; -import tanks.event.EventUpdateCoins; -import tanks.gui.ChatMessage; import tanks.gui.IFixedMenu; import tanks.gui.Scoreboard; -import tanks.gui.screen.ScreenGame; -import tanks.gui.screen.ScreenPartyHost; import tanks.gui.screen.ScreenPartyLobby; import tanks.hotbar.item.Item; import tanks.hotbar.item.ItemMine; -import tanks.obstacle.Obstacle; import java.util.ArrayList; import java.util.HashMap; @@ -22,6 +15,7 @@ public class Mine extends Movable implements IAvoidObject { public static double mine_size = 30; + public static double mine_radius = Game.tile_size * 2.5; public double timer; public double size = mine_size; @@ -34,7 +28,7 @@ public class Mine extends Movable implements IAvoidObject public double damage = 2; public boolean destroysObstacles = true; - public double radius = Game.tile_size * 2.5; + public double radius = mine_radius; public Tank tank; public Item item; public boolean exploded = false; @@ -204,140 +198,22 @@ public void update() public void explode() { - Drawing.drawing.playSound("explosion.ogg", (float) (mine_size / this.size)); - this.exploded = true; - - if (Game.effectsEnabled) - { - for (int j = 0; j < 200 * this.radius / 125 * Game.effectMultiplier; j++) - { - double random = Math.random(); - Effect e = Effect.createNewEffect(this.posX, this.posY, Effect.EffectType.piece); - e.maxAge /= 2; - e.colR = 255; - e.colG = (1 - random) * 155 + Math.random() * 100; - e.colB = 0; - - if (Game.enable3d) - e.set3dPolarMotion(Math.random() * 2 * Math.PI, Math.random() * Math.PI / 2, random * (this.radius - Game.tile_size / 2) / Game.tile_size * 2); - else - e.setPolarMotion(Math.random() * 2 * Math.PI, random * (this.radius - Game.tile_size / 2) / Game.tile_size * 2); - Game.effects.add(e); - } - } + Game.eventsOut.add(new EventMineExplode(this)); + Game.removeMovables.add(this); - this.destroy = true; + freeIDs.add(this.networkID); + idMap.remove(this.networkID); if (!ScreenPartyLobby.isClient) { - Game.eventsOut.add(new EventMineExplode(this)); + Explosion e = new Explosion(this); + e.explode(); - for (Movable m: Game.movables) - { - if (Math.pow(Math.abs(m.posX - this.posX), 2) + Math.pow(Math.abs(m.posY - this.posY), 2) < Math.pow(radius, 2)) - { - if (m instanceof Tank && !m.destroy && ((Tank) m).getDamageMultiplier(this) > 0) - { - if (!(Team.isAllied(this, m) && !this.team.friendlyFire) && !ScreenGame.finishedQuick) - { - Tank t = (Tank) m; - boolean kill = t.damage(this.damage, this); - - if (kill) - { - if (Game.currentLevel instanceof ModLevel) - { - for (IFixedMenu menu : ModAPI.menuGroup) - { - if (menu instanceof Scoreboard && ((Scoreboard) menu).objectiveType.equals(Scoreboard.objectiveTypes.kills)) - { - if (!((Scoreboard) menu).teams.isEmpty()) - ((Scoreboard) menu).addTeamScore(this.tank.team, 1); - - else if (this.tank instanceof TankPlayer && !((Scoreboard) menu).players.isEmpty()) - ((Scoreboard) menu).addPlayerScore(((TankPlayer) this.tank).player, 1); - - else if (this.tank instanceof TankPlayerRemote && !((Scoreboard) menu).players.isEmpty()) - ((Scoreboard) menu).addPlayerScore(((TankPlayerRemote) this.tank).player, 1); - } - } - - if (((ModLevel) Game.currentLevel).enableKillMessages && ScreenPartyHost.isServer) - { - String message = ((ModLevel) Game.currentLevel).generateKillMessage(t, this.tank, false); - ScreenPartyHost.chat.add(0, new ChatMessage(message)); - Game.eventsOut.add(new EventChat(message)); - } - } - - - if (this.tank.equals(Game.playerTank)) - { - if (Game.currentLevel instanceof ModLevel && (t instanceof TankPlayer || t instanceof TankPlayerRemote)) - Game.player.hotbar.coins += ((ModLevel) Game.currentLevel).playerKillCoins; - else - Game.player.hotbar.coins += t.coinValue; - } - else if (this.tank instanceof TankPlayerRemote && (Crusade.crusadeMode || Game.currentLevel.shop.size() > 0 || Game.currentLevel.startingItems.size() > 0)) - { - if (t instanceof TankPlayer || t instanceof TankPlayerRemote) - { - if (Game.currentLevel instanceof ModLevel && ((ModLevel) Game.currentLevel).playerKillCoins > 0) - ((TankPlayerRemote) this.tank).player.hotbar.coins += ((ModLevel) Game.currentLevel).playerKillCoins; - else - ((TankPlayerRemote) this.tank).player.hotbar.coins += t.coinValue; - } - Game.eventsOut.add(new EventUpdateCoins(((TankPlayerRemote) this.tank).player)); - } - } - else - Drawing.drawing.playGlobalSound("damage.ogg"); - } - } - else if (m instanceof Mine && !m.destroy) - { - if (((Mine) m).timer > 10 && !this.isRemote) - { - ((Mine) m).timer = 10; - Game.eventsOut.add(new EventMineChangeTimer((Mine) m)); - } - } - else if (m instanceof Bullet && !m.destroy) - { - m.destroy = true; - } - } - } - } - - if (this.destroysObstacles) - { - for (Obstacle o: Game.obstacles) - { - if (Math.pow(Math.abs(o.posX - this.posX), 2) + Math.pow(Math.abs(o.posY - this.posY), 2) < Math.pow(radius, 2) && o.destructible && !Game.removeObstacles.contains(o)) - { - o.onDestroy(this); - o.playDestroyAnimation(this.posX, this.posY, this.radius); - } - } - } - - if (!ScreenPartyLobby.isClient) - { if (!(this.item instanceof ItemMine)) this.tank.liveMines--; else ((ItemMine) this.item).liveMines--; } - - Effect e = Effect.createNewEffect(this.posX, this.posY, Effect.EffectType.mineExplosion); - e.radius = Math.max(this.radius - Game.tile_size * 0.5, 0); - Game.effects.add(e); - - Game.removeMovables.add(this); - - freeIDs.add(this.networkID); - idMap.remove(this.networkID); } @Override diff --git a/src/main/java/tanks/tank/Tank.java b/src/main/java/tanks/tank/Tank.java index 15233e0b..dc1eb957 100755 --- a/src/main/java/tanks/tank/Tank.java +++ b/src/main/java/tanks/tank/Tank.java @@ -344,7 +344,7 @@ public void update() e.colB = Math.min(255, Math.max(0, this.colorB + Math.random() * var - var / 2)); if (Game.enable3d) - e.set3dPolarMotion(Math.random() * 2 * Math.PI, Math.random() * Math.PI, Math.random() * this.size / 50.0); + e.set3dPolarMotion(Math.random() * 2 * Math.PI, Math.atan(Math.random()), Math.random() * this.size / 50.0); else e.setPolarMotion(Math.random() * 2 * Math.PI, Math.random() * this.size / 50.0); @@ -794,8 +794,8 @@ public boolean damage(double amount, IGameObject source) if (source instanceof Bullet) owner = ((Bullet) source).tank; - else if (source instanceof Mine) - owner = ((Mine) source).tank; + else if (source instanceof Explosion) + owner = ((Explosion) source).tank; else if (source instanceof Tank) owner = (Tank) source; @@ -831,7 +831,7 @@ public void checkHit(Tank owner, IGameObject source) cp.addKill(this); } - if (cp != null && (source instanceof Bullet || source instanceof Mine)) + if (cp != null && (source instanceof Bullet || source instanceof Explosion)) cp.addItemHit(source); } @@ -852,7 +852,7 @@ public void checkHit(Tank owner, IGameObject source) public double getDamageMultiplier(IGameObject source) { - if (this.invulnerable || (source instanceof Bullet && this.resistBullets) || (source instanceof Mine && this.resistExplosions)) + if (this.invulnerable || (source instanceof Bullet && this.resistBullets) || (source instanceof Explosion && this.resistExplosions)) return 0; return 1; diff --git a/src/main/java/tanks/tank/TankAIControlled.java b/src/main/java/tanks/tank/TankAIControlled.java index 590657d6..5605dc40 100755 --- a/src/main/java/tanks/tank/TankAIControlled.java +++ b/src/main/java/tanks/tank/TankAIControlled.java @@ -110,6 +110,7 @@ protected enum RotationPhase {clockwise, counterClockwise, aiming} /** Type of shooting AI to use*/ public ShootAI shootAIType; + public String shotSound = null; // The following are values which are internally used for carrying out behavior. // These values change constantly during the course of the game. @@ -326,6 +327,9 @@ public void launchBullet(double offset) { Drawing.drawing.playGlobalSound("shoot.ogg", (float) (Bullet.bullet_size / this.bulletSize)); + if (this.shotSound != null) + Drawing.drawing.playGlobalSound(this.shotSound, (float) (Bullet.bullet_size / this.bulletSize)); + Bullet b = new Bullet(this.posX, this.posY, this.bulletBounces, this); b.setPolarMotion(angle + offset, this.bulletSpeed); b.moveOut(50 / this.bulletSpeed * this.size / Game.tile_size); @@ -631,7 +635,7 @@ public void checkForBulletThreats() if (Game.movables.get(i) instanceof Bullet && !Game.movables.get(i).destroy) { Bullet b = (Bullet) Game.movables.get(i); - if (!(b.tank == this && b.age < 20) && b.shouldDodge && Math.abs(b.posX - this.posX) < Game.tile_size * 10 && Math.abs(b.posY - this.posY) < Game.tile_size * 10 && b.getMotionInDirection(b.getAngleInDirection(this.posX, this.posY)) > 0) + if (!(b.tank == this && b.age < 20) && !(this.team != null && Team.isAllied(b, this) && !this.team.friendlyFire) && b.shouldDodge && Math.abs(b.posX - this.posX) < Game.tile_size * 10 && Math.abs(b.posY - this.posY) < Game.tile_size * 10 && b.getMotionInDirection(b.getAngleInDirection(this.posX, this.posY)) > 0) { Ray r = b.getRay(); r.tankHitSizeMul = 4; @@ -1067,7 +1071,7 @@ public void updateMineAI() for (int i = 0; i < Game.movables.size(); i++) { Movable m = Game.movables.get(i); - if (m instanceof Mine) + if (m instanceof Mine && !(this.team != null && Team.isAllied(this, m) && !this.team.friendlyFire)) { if (Math.pow(m.posX - this.posX, 2) + Math.pow(m.posY - this.posY, 2) <= Math.pow(((Mine)m).radius * this.avoidSensitivity, 2)) { diff --git a/src/main/java/tanks/tank/TankGold.java b/src/main/java/tanks/tank/TankGold.java index 95ab413c..fdb00279 100755 --- a/src/main/java/tanks/tank/TankGold.java +++ b/src/main/java/tanks/tank/TankGold.java @@ -84,10 +84,8 @@ public void postUpdate() if (this.timeUntilDeath <= 0) { - Mine m = new Mine(this.posX, this.posY, 0, this); - m.radius *= 1.5; - Game.eventsOut.add(new EventLayMine(m)); - Game.movables.add(m); + Explosion e = new Explosion(this.posX, this.posY, Mine.mine_radius * 1.5, 2, true, this); + e.explode(); this.destroy = true; this.health = 0; } diff --git a/src/main/java/tanks/tank/TankGreen.java b/src/main/java/tanks/tank/TankGreen.java index e9b42077..31c34e97 100755 --- a/src/main/java/tanks/tank/TankGreen.java +++ b/src/main/java/tanks/tank/TankGreen.java @@ -24,7 +24,7 @@ public TankGreen(String name, double x, double y, double angle) this.turretIdleTimerRandom = 500; this.enableLookingAtTargetEnemy = false; this.enableDefensiveFiring = true; - + this.coinValue = 10; this.description = "A deadly stationary tank which---shoots rockets that bounce twice"; diff --git a/src/main/java/tanks/tank/TankMedic.java b/src/main/java/tanks/tank/TankMedic.java index 25969fe9..700f778b 100755 --- a/src/main/java/tanks/tank/TankMedic.java +++ b/src/main/java/tanks/tank/TankMedic.java @@ -87,9 +87,8 @@ public void postUpdate() if (this.timeUntilDeath <= 0) { - Mine m = new Mine(this.posX, this.posY, 0, this); - Game.eventsOut.add(new EventLayMine(m)); - Game.movables.add(m); + Explosion e = new Explosion(this.posX, this.posY, Mine.mine_radius, 2, true, this); + e.explode(); this.destroy = true; this.health = 0; } diff --git a/src/main/java/tanks/tank/TankMustard.java b/src/main/java/tanks/tank/TankMustard.java index 0ad35f6e..60eb5615 100755 --- a/src/main/java/tanks/tank/TankMustard.java +++ b/src/main/java/tanks/tank/TankMustard.java @@ -25,6 +25,7 @@ public TankMustard(String name, double x, double y, double angle) this.enableLookingAtTargetEnemy = false; this.cooldownBase = 200; this.cooldownRandom = 100; + this.bulletBounces = 0; this.coinValue = 4; this.turret.size *= 1.75; @@ -104,11 +105,12 @@ public void shoot() Drawing.drawing.playGlobalSound("arc.ogg", 1 / 2.5f); - BulletArc b = new BulletArc(this.posX, this.posY, 5, this); + BulletArc b = new BulletArc(this.posX, this.posY, 0, this); b.team = this.team; b.addPolarMotion(this.aimAngle, this.bulletSpeed); b.vZ = this.distance / this.bulletSpeed * 0.5 * BulletArc.gravity; b.size = 25; + b.bounces = this.bulletBounces; Game.eventsOut.add(new EventShootBullet(b)); Game.movables.add(b); diff --git a/src/main/java/tanks/tank/TankOrangeRed.java b/src/main/java/tanks/tank/TankOrangeRed.java index d0400b74..930b0375 100755 --- a/src/main/java/tanks/tank/TankOrangeRed.java +++ b/src/main/java/tanks/tank/TankOrangeRed.java @@ -81,7 +81,6 @@ public void shoot() Ray a2 = new Ray(this.posX, this.posY, an, this.bulletBounces, this); a2.size = this.bulletSize; - // TODO figure out why this is necessary or if there's no point in getting the target of the ray a2.getTarget(); a2.ignoreDestructible = this.ignoreDestructible; @@ -106,7 +105,7 @@ public void shoot() { for (Movable m2: Game.movables) { - if (Team.isAllied(m2, this) && m2 instanceof Tank && !((Tank) m2).resistExplosions && this.team.friendlyFire && Math.pow(m2.posX - a.posX, 2) + Math.pow(m2.posY - a.posY, 2) <= Math.pow(Game.tile_size * 2.5, 2)) + if (Team.isAllied(m2, this) && m2 instanceof Tank && !((Tank) m2).resistExplosions && this.team.friendlyFire && Math.pow(m2.posX - a.posX, 2) + Math.pow(m2.posY - a.posY, 2) <= Math.pow(Mine.mine_size, 2)) return; } @@ -118,8 +117,7 @@ public void shoot() @Override public void onDestroy() { - Mine m = new Mine(this.posX, this.posY, 0, this); - Game.eventsOut.add(new EventLayMine(m)); - Game.movables.add(m); + Explosion e = new Explosion(this.posX, this.posY, Mine.mine_radius, 2, true, this); + e.explode(); } } diff --git a/src/main/java/tanks/tank/TankPink.java b/src/main/java/tanks/tank/TankPink.java index 0776ab95..953b0d7b 100755 --- a/src/main/java/tanks/tank/TankPink.java +++ b/src/main/java/tanks/tank/TankPink.java @@ -30,7 +30,7 @@ public TankPink(String name, double x, double y, double angle) this.turretIdleTimerBase = 25; this.turretIdleTimerRandom = 500; this.enableLookingAtTargetEnemy = false; - + this.coinValue = 12; this.description = "A tank which spawns---mini tanks and shoots---2-bounce rockets"; diff --git a/src/main/java/tanks/tank/TankTest.java b/src/main/java/tanks/tank/TankTest.java new file mode 100755 index 00000000..757a56d2 --- /dev/null +++ b/src/main/java/tanks/tank/TankTest.java @@ -0,0 +1,37 @@ +package tanks.tank; + +import tanks.Game; +import tanks.bullet.Bullet; + +/** + * A primitive stationary tank + */ +public class TankTest extends TankAIControlled +{ + + public TankTest(String name, double x, double y, double angle) + { + super(name, x, y, Game.tile_size, 200, 140, 60, angle, ShootAI.straight); + + this.enableMovement = false; + this.enableMineLaying = false; + this.liveBulletMax = 5; + this.cooldownRandom = 0; + this.cooldownBase = 20; + this.idleTurretSpeed = 0.01; + this.bulletBounces = 1; + this.turretIdleTimerBase = 500; + this.turretIdleTimerRandom = 500; + this.enableLookingAtTargetEnemy = false; + this.enablePredictiveFiring = true; + this.enableDefensiveFiring = true; + + this.bulletBounces = 0; + this.bulletEffect = Bullet.BulletEffect.fire; + this.bulletSpeed = 25.0 / 4; + + this.coinValue = 5; + + this.description = "A primitive stationary tank"; + } +} diff --git a/src/main/resources/crusades/adventure_crusade.tanks b/src/main/resources/crusades/adventure_crusade.tanks index 75818388..c4043cbb 100755 --- a/src/main/resources/crusades/adventure_crusade.tanks +++ b/src/main/resources/crusades/adventure_crusade.tanks @@ -10,7 +10,7 @@ Shield,shield.png,50,19,1,10,shield,1.0,5.0,50.0 Freezing bullet,bullet_freeze.png,10,21,1,25,bullet,freezing,ice,3.125,0,0.25,1,50.0,10.0,1.0,false Flamethrower,bullet_flame.png,4,23,100,1000,bullet,flamethrower,none,3.125,0,1.0,0,0.0,10.0,0.0,false Mega bullet,bullet_large.png,15,25,2,20,bullet,normal,trail,3.125,3,1.0,5,100.0,25.0,4.0,true -Healing ray,bullet_healing.png,25,27,100,5000,bullet,healing,none,3.125,1,0.01,0,0.0,10.0,0.0,false +Healing ray,bullet_healing.png,25,27,100,5000,bullet,healing,none,3.125,0,0.01,0,0.0,10.0,0.0,false Mini bullet,bullet_mini.png,5,29,20,500,bullet,normal,trail,6.25,0,0.125,8,5.0,5.0,1.0,false Dark fire bullet,bullet_dark_fire.png,10,31,5,250,bullet,normal,dark_fire,6.25,0,1.0,5,30.0,10.0,1.0,false levels diff --git a/src/main/resources/items/items.tanks b/src/main/resources/items/items.tanks index f8572569..01af3993 100755 --- a/src/main/resources/items/items.tanks +++ b/src/main/resources/items/items.tanks @@ -9,8 +9,8 @@ Shield,shield.png,50,0,1,10,shield,1.0,5.0,50.0 Freezing bullet,bullet_freeze.png,10,0,1,25,bullet,freezing,ice,3.125,0,0.25,1,50.0,10.0,1.0,false Flamethrower,bullet_flame.png,4,0,100,1000,bullet,flamethrower,none,3.125,0,1.0,0,0.0,10.0,0.0,false Mega bullet,bullet_large.png,15,0,2,20,bullet,normal,trail,3.125,3,1.0,5,100.0,25.0,4.0,true -Artillery shell,bullet_arc.png,5,0,2,40,bullet,arc,none,3.125,1,1.0,4,60.0,25.0,0.0,false -Healing ray,bullet_healing.png,25,0,100,5000,bullet,healing,none,3.125,1,0.01,0,0.0,10.0,0.0,false +Artillery shell,bullet_arc.png,5,0,2,40,bullet,arc,none,3.125,0,1.0,4,60.0,25.0,0.0,false +Healing ray,bullet_healing.png,25,0,100,5000,bullet,healing,none,3.125,0,0.01,0,0.0,10.0,0.0,false Explosive bullet,bullet_explosive.png,10,0,2,25,bullet,explosive,trail,3.125,0,1.0,2,75.0,20.0,1.0,false Booster,bullet_boost.png,10,0,10,100,bullet,boost,ember,6.25,0,1.0,5,20.0,10.0,1.0,false Mini bullet,bullet_mini.png,5,0,20,500,bullet,normal,trail,6.25,0,0.125,8,5.0,5.0,1.0,false