├── .gitignore ├── README.md ├── assets ├── backgrounds │ ├── bg01-hd.png │ ├── bg01.png │ ├── bg02-hd.png │ ├── bg02.png │ ├── bg03-hd.png │ ├── bg03.png │ ├── bg04-hd.png │ ├── bg04.png │ ├── bg05-hd.png │ └── bg05.png ├── bigSprites.png ├── coverImage.png ├── grounds │ ├── ground01.png │ ├── ground02.png │ ├── ground03.png │ ├── ground04.png │ └── ground05.png ├── player │ ├── layerOne.png │ ├── layerThree.png │ ├── layerTwo.png │ └── spaceship.png ├── portal.png ├── smallBlocks.png ├── spikes.png ├── spritesheet.png ├── stereoMadness.mp3 ├── stereoMadness.wav └── ui │ ├── bigButtons.png │ ├── buttonSprites.png │ ├── menuContainerBackground.png │ └── tabs.png └── src └── com └── jade ├── Main.java ├── components ├── BoxBounds.java ├── Component.java ├── Sprite.java ├── SubSprite.java └── TriangleBounds.java ├── dataStructures ├── ConnectionGene.java ├── Genome.java ├── InnovationGenerator.java ├── JColor.java ├── JImage.java ├── JMath.java ├── JString.java ├── Matrix.java ├── NodeGene.java ├── Pool.java ├── SpecieManager.java ├── Species.java └── Vector2.java ├── file ├── Parser.java └── Serialize.java ├── jade ├── AssetPool.java ├── Camera.java ├── GameObject.java ├── Renderer.java └── Transform.java ├── main ├── Constants.java ├── KL.java ├── LevelEditorScene.java ├── LevelScene.java ├── ML.java ├── Scene.java ├── Time.java └── Window.java ├── scripts ├── Button.java ├── CameraControls.java ├── CursorScript.java ├── EditorKeyShortcuts.java ├── Ground.java ├── Music.java ├── ParallaxBackground.java ├── Player.java ├── Portal.java ├── RotateButton.java └── SnapToGrid.java └── ui ├── Grid.java ├── MainContainer.java ├── MenuItem.java └── TabItem.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* 2 | 3 | levels/* 4 | 5 | out/* 6 | 7 | drawing.svg 8 | 9 | GeometryDash.iml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Geometry Dash in Java 2 | Currently supports level editing for one level. You can edit the source code and build multiple levels 3 | fairly easily. I just haven't built in a GUI support system for that yet. 4 | 5 | ## Controls 6 | * F2 to play from edit mode 7 | * F1 to exit play mode and enter edit mode 8 | 9 | ## Controls in play mode 10 | * Space to jump 11 | * Space to fly 12 | * F1 to exit 13 | 14 | ## Controls in edit mode 15 | * Ctrl+D to duplicate 16 | * Press scroll button on mouse to pan camera 17 | * Shift+Arrow_keys to move objects 1/10 of a block 18 | * Arrow keys to move selected objects 19 | * Mouse click to select individual objects, or clear selection 20 | * Mouse drag to select multiple objects 21 | * Rotate buttons to rotate selected objects 22 | * Delete to delete selected objects -------------------------------------------------------------------------------- /assets/backgrounds/bg01-hd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/backgrounds/bg01-hd.png -------------------------------------------------------------------------------- /assets/backgrounds/bg01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/backgrounds/bg01.png -------------------------------------------------------------------------------- /assets/backgrounds/bg02-hd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/backgrounds/bg02-hd.png -------------------------------------------------------------------------------- /assets/backgrounds/bg02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/backgrounds/bg02.png -------------------------------------------------------------------------------- /assets/backgrounds/bg03-hd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/backgrounds/bg03-hd.png -------------------------------------------------------------------------------- /assets/backgrounds/bg03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/backgrounds/bg03.png -------------------------------------------------------------------------------- /assets/backgrounds/bg04-hd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/backgrounds/bg04-hd.png -------------------------------------------------------------------------------- /assets/backgrounds/bg04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/backgrounds/bg04.png -------------------------------------------------------------------------------- /assets/backgrounds/bg05-hd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/backgrounds/bg05-hd.png -------------------------------------------------------------------------------- /assets/backgrounds/bg05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/backgrounds/bg05.png -------------------------------------------------------------------------------- /assets/bigSprites.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/bigSprites.png -------------------------------------------------------------------------------- /assets/coverImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/coverImage.png -------------------------------------------------------------------------------- /assets/grounds/ground01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/grounds/ground01.png -------------------------------------------------------------------------------- /assets/grounds/ground02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/grounds/ground02.png -------------------------------------------------------------------------------- /assets/grounds/ground03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/grounds/ground03.png -------------------------------------------------------------------------------- /assets/grounds/ground04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/grounds/ground04.png -------------------------------------------------------------------------------- /assets/grounds/ground05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/grounds/ground05.png -------------------------------------------------------------------------------- /assets/player/layerOne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/player/layerOne.png -------------------------------------------------------------------------------- /assets/player/layerThree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/player/layerThree.png -------------------------------------------------------------------------------- /assets/player/layerTwo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/player/layerTwo.png -------------------------------------------------------------------------------- /assets/player/spaceship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/player/spaceship.png -------------------------------------------------------------------------------- /assets/portal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/portal.png -------------------------------------------------------------------------------- /assets/smallBlocks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/smallBlocks.png -------------------------------------------------------------------------------- /assets/spikes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/spikes.png -------------------------------------------------------------------------------- /assets/spritesheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/spritesheet.png -------------------------------------------------------------------------------- /assets/stereoMadness.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/stereoMadness.mp3 -------------------------------------------------------------------------------- /assets/stereoMadness.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/stereoMadness.wav -------------------------------------------------------------------------------- /assets/ui/bigButtons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/ui/bigButtons.png -------------------------------------------------------------------------------- /assets/ui/buttonSprites.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/ui/buttonSprites.png -------------------------------------------------------------------------------- /assets/ui/menuContainerBackground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/ui/menuContainerBackground.png -------------------------------------------------------------------------------- /assets/ui/tabs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ambrosiogabe/JavaGeometryDash/c36740f66c9494da5871b8b290a9dcc751ee9db1/assets/ui/tabs.png -------------------------------------------------------------------------------- /src/com/jade/Main.java: -------------------------------------------------------------------------------- 1 | package com.jade; 2 | 3 | import com.jade.main.Window; 4 | 5 | public class Main { 6 | public static void main(String[] args) { 7 | Window window = Window.getWindow(); 8 | window.init(); 9 | 10 | Thread t1 = new Thread(window); 11 | t1.start(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/com/jade/components/BoxBounds.java: -------------------------------------------------------------------------------- 1 | package com.jade.components; 2 | 3 | import com.jade.dataStructures.Genome; 4 | import com.jade.dataStructures.JString; 5 | import com.jade.dataStructures.Vector2; 6 | import com.jade.jade.GameObject; 7 | import com.jade.file.Parser; 8 | import com.jade.main.Constants; 9 | import com.jade.main.LevelScene; 10 | import com.jade.main.Window; 11 | import com.jade.scripts.Player; 12 | 13 | import java.awt.Graphics2D; 14 | import java.awt.Color; 15 | import java.awt.BasicStroke; 16 | import java.awt.geom.Rectangle2D; 17 | 18 | public class BoxBounds extends Component { 19 | public double width, height; 20 | public double angle = 0.0; 21 | 22 | public boolean isSelected = false; 23 | 24 | public boolean onGround = true; 25 | public boolean isDeathBox; 26 | public boolean canCollide; 27 | public Vector2 velocity; 28 | public Vector2 acceleration; 29 | 30 | public boolean isPlaying; 31 | public float halfWidth; 32 | public float halfHeight; 33 | 34 | public float xBuffer, yBuffer; 35 | 36 | public BoxBounds(double width, double height, double angle, boolean isPlaying, boolean deathBox, boolean canCollide) { 37 | this.width = width; 38 | this.height = height; 39 | this.isPlaying = isPlaying; 40 | this.angle = angle; 41 | this.halfWidth = (float)(width / 2.0); 42 | this.halfHeight = (float)(height / 2.0); 43 | if (width <= Constants.TILE_WIDTH) 44 | this.xBuffer = (float)((Constants.TILE_WIDTH - width) / 2.0); 45 | else 46 | this.xBuffer = (float)((Constants.TILE_WIDTH * 2 - width) / 2.0); 47 | if (height <= Constants.TILE_WIDTH) 48 | this.yBuffer = (float)(Constants.TILE_WIDTH - height); 49 | else 50 | this.yBuffer = (float)(Constants.TILE_WIDTH * 2 - height); 51 | this.isDeathBox = deathBox; 52 | this.canCollide = canCollide; 53 | } 54 | 55 | public BoxBounds(double width, double height, boolean isPlaying, boolean deathBox) { 56 | this.width = width; 57 | this.height = height; 58 | this.isPlaying = isPlaying; 59 | this.angle = angle; 60 | if (width <= Constants.TILE_WIDTH) 61 | this.xBuffer = (float)((Constants.TILE_WIDTH - width) / 2.0); 62 | else 63 | this.xBuffer = (float)((Constants.TILE_WIDTH * 2 - width) / 2.0); 64 | if (height <= Constants.TILE_WIDTH) 65 | this.yBuffer = (float)(Constants.TILE_WIDTH - height); 66 | else 67 | this.yBuffer = (float)(Constants.TILE_WIDTH * 2 - height); 68 | this.isDeathBox = deathBox; 69 | this.canCollide = true; 70 | } 71 | 72 | public BoxBounds clone() { 73 | return new BoxBounds(width, height, angle, isPlaying, isDeathBox, canCollide); 74 | } 75 | 76 | @Override 77 | public void start() { 78 | this.velocity = new Vector2(0, 0); 79 | this.acceleration = new Vector2(0, 0); 80 | this.halfWidth = (float)this.width / 2.0f; 81 | this.halfHeight = (float)this.height / 2.0f; 82 | } 83 | 84 | @Override 85 | public void update(double dt) { 86 | if (!isPlaying) { 87 | if (!Window.isEditing && canCollide) { 88 | resolveCollision(LevelScene.getScene().player); 89 | } 90 | return; 91 | } 92 | 93 | parent.transform.position = Vector2.add(parent.transform.position, Vector2.scale(velocity, dt)); 94 | velocity = Vector2.add(velocity, Vector2.scale(acceleration, dt)); 95 | velocity = Vector2.add(velocity, Vector2.scale(new Vector2(0.0f, Constants.GRAVITY), dt)); 96 | 97 | if (velocity.lengthSquared() > Constants.TERMINAL_VELOCITY * Constants.TERMINAL_VELOCITY) { 98 | velocity = Vector2.normalize(velocity); 99 | velocity = Vector2.scale(velocity, Constants.TERMINAL_VELOCITY); 100 | } 101 | } 102 | 103 | public boolean pointInSquare(float x, float y) { 104 | return x >= this.parent.transform.position.x + xBuffer && x <= this.parent.transform.position.x + this.width + xBuffer && 105 | y >= this.parent.transform.position.y + yBuffer && y <= this.parent.transform.position.y + this.height + yBuffer; 106 | } 107 | 108 | public boolean isContainedInRectangle(float x, float y, float w, float h) { 109 | return this.parent.transform.position.x + xBuffer >= x && this.parent.transform.position.x + this.width + xBuffer <= x + w && 110 | this.parent.transform.position.y + yBuffer >= y && this.parent.transform.position.y + this.height + yBuffer <= y + h; 111 | } 112 | 113 | private void resolveCollision(GameObject plr) { 114 | float dx = (plr.transform.position.x + plr.getComponent(BoxBounds.class).halfWidth) - (this.parent.transform.position.x + xBuffer + this.halfWidth); 115 | float dy = (plr.transform.position.y + plr.getComponent(BoxBounds.class).halfHeight) - (this.parent.transform.position.y + yBuffer + this.halfHeight); 116 | 117 | float combinedHalfWidths = plr.getComponent(BoxBounds.class).halfWidth + this.halfWidth; 118 | float combinedHalfHeights = plr.getComponent(BoxBounds.class).halfHeight + this.halfHeight; 119 | 120 | if (Math.abs(dx) < combinedHalfWidths) { 121 | if (Math.abs(dy) < combinedHalfHeights) { 122 | float overlapX = combinedHalfWidths - Math.abs(dx); 123 | float overlapY = combinedHalfHeights - Math.abs(dy); 124 | 125 | if (overlapX >= overlapY) { 126 | if (dy > 0) { 127 | // Collision on the bottom of box 128 | if (!isDeathBox) 129 | plr.transform.position.y = this.parent.transform.position.y + Constants.GRID_HEIGHT; 130 | else { 131 | plr.getComponent(Player.class).die(); 132 | } 133 | } else { 134 | // Collision on the top of box 135 | if (!isDeathBox) { 136 | if (plr.getComponent(Player.class).isJumping) { 137 | plr.transform.position.y = this.parent.transform.position.y + this.yBuffer - Constants.PLR_HEIGHT; 138 | } 139 | plr.getComponent(BoxBounds.class).velocity.y = 0; 140 | plr.getComponent(BoxBounds.class).onGround = true; 141 | } else { 142 | plr.getComponent(Player.class).die(); 143 | } 144 | } 145 | } else if (Math.abs(dx) < 40) { 146 | // if (plr.getComponent(Player.class).isAi) { 147 | // plr.getComponent(Player.class).die(); 148 | // return; 149 | // } 150 | 151 | if (dx > 0) { 152 | if (!isDeathBox && dy <= 0.3) { 153 | if (plr.getComponent(Player.class).isJumping) return; 154 | plr.transform.position.y = this.parent.transform.position.y + this.yBuffer - Constants.PLR_HEIGHT; 155 | plr.getComponent(BoxBounds.class).velocity.y = 0; 156 | plr.getComponent(BoxBounds.class).onGround = true; 157 | } else if (!isDeathBox && dy >= 37.0) { 158 | plr.transform.position.y = this.parent.transform.position.y + this.yBuffer + (float)height + 0.4f; 159 | plr.getComponent(BoxBounds.class).velocity.y = 0; 160 | plr.getComponent(BoxBounds.class).onGround = true; 161 | } else { 162 | // Collision on the left 163 | plr.getComponent(Player.class).die(); 164 | } 165 | } else if (dx < 0) { 166 | if (!isDeathBox && dy <= 0.3) { 167 | if (plr.getComponent(Player.class).isJumping) return; 168 | plr.transform.position.y = this.parent.transform.position.y + this.yBuffer - Constants.PLR_HEIGHT; 169 | plr.getComponent(BoxBounds.class).velocity.y = 0; 170 | plr.getComponent(BoxBounds.class).onGround = true; 171 | } else { 172 | // Collision on the right 173 | plr.getComponent(Player.class).die(); 174 | } 175 | } 176 | } 177 | } 178 | } 179 | } 180 | 181 | public boolean isColliding(BoxBounds bounds, Vector2 pos) { 182 | // bounds.centerx - this.centerx 183 | float dx = (pos.x + bounds.halfWidth) - (this.parent.transform.position.x + xBuffer + this.halfWidth); 184 | // bounds.centery - this.centery 185 | float dy = (pos.y + bounds.halfHeight) - (this.parent.transform.position.y + yBuffer + this.halfHeight); 186 | 187 | float combinedHalfWidths = bounds.halfWidth + this.halfWidth; 188 | float combinedHalfHeights = bounds.halfHeight + this.halfHeight; 189 | 190 | if (Math.abs(dx) < combinedHalfWidths) { 191 | return Math.abs(dy) < combinedHalfHeights; 192 | } 193 | return false; 194 | } 195 | 196 | @Override 197 | public void draw(Graphics2D g2) { 198 | g2.setColor(Color.GREEN); 199 | if (isSelected) { 200 | g2.setStroke(new BasicStroke(3)); 201 | Graphics2D oldGraphics = (Graphics2D)g2.create(); 202 | oldGraphics.translate(parent.transform.position.x + xBuffer, parent.transform.position.y + yBuffer); 203 | oldGraphics.rotate(Math.toRadians(angle), width / 2.0, height / 2.0); 204 | oldGraphics.draw(new Rectangle2D.Double(0, 0, width, height)); 205 | } 206 | } 207 | 208 | @Override 209 | public String serialize(int tabSize) { 210 | StringBuilder builder = JString.getBuilder(); 211 | 212 | builder.append(beginObjectProperty("BoxBounds", tabSize)); 213 | builder.append(addDoubleProperty("width", width, tabSize + 1, true, true)); 214 | builder.append(addDoubleProperty("height", height, tabSize + 1, true, true)); 215 | builder.append(addDoubleProperty("angle", angle, tabSize + 1, true, true)); 216 | builder.append(addBooleanProperty("isPlaying", isPlaying, tabSize + 1, true, true)); 217 | builder.append(addBooleanProperty("canCollide", canCollide, tabSize + 1, true, true)); 218 | builder.append(addBooleanProperty("isDeathBox", isDeathBox, tabSize + 1, true, false)); 219 | builder.append(closeObjectProperty(tabSize)); 220 | 221 | return builder.toString(); 222 | } 223 | 224 | public static BoxBounds deserialize() { 225 | Parser.consumeBeginObjectProperty(); 226 | 227 | double width = Parser.consumeDoubleProperty("width"); 228 | Parser.consume(','); 229 | double height = Parser.consumeDoubleProperty("height"); 230 | Parser.consume(','); 231 | double angle = Parser.consumeDoubleProperty("angle"); 232 | Parser.consume(','); 233 | boolean isPlaying = Parser.consumeBooleanProperty("isPlaying"); 234 | Parser.consume(','); 235 | boolean canCollide = Parser.consumeBooleanProperty("canCollide"); 236 | Parser.consume(','); 237 | boolean deathBox = Parser.consumeBooleanProperty("isDeathBox"); 238 | Parser.consume('}'); 239 | 240 | return new BoxBounds(width, height, angle, isPlaying, deathBox, canCollide); 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /src/com/jade/components/Component.java: -------------------------------------------------------------------------------- 1 | package com.jade.components; 2 | 3 | import com.jade.jade.GameObject; 4 | import com.jade.file.Serialize; 5 | 6 | import java.awt.*; 7 | 8 | public abstract class Component extends Serialize { 9 | public GameObject parent; 10 | 11 | public void draw(Graphics2D g2) { 12 | return; 13 | } 14 | 15 | public void update(double dt) { 16 | return; 17 | } 18 | 19 | public void start() { 20 | return; 21 | } 22 | 23 | public abstract Component clone(); 24 | 25 | @Override 26 | public String serialize(int tabSize) { 27 | return ""; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/com/jade/components/Sprite.java: -------------------------------------------------------------------------------- 1 | package com.jade.components; 2 | 3 | import com.jade.dataStructures.JString; 4 | import com.jade.jade.AssetPool; 5 | 6 | import javax.imageio.ImageIO; 7 | import java.awt.*; 8 | import java.awt.image.BufferedImage; 9 | import java.io.File; 10 | import java.io.IOException; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class Sprite extends Component { 15 | public BufferedImage image; 16 | public String pictureFile; 17 | public boolean isSpritesheet = false; 18 | public int tileWidth = 0; 19 | public int tileHeight = 0; 20 | public int width = 0, height = 0; 21 | public int spacing = 0; 22 | public int columns = 0; 23 | public int gid = 1; 24 | public int size; 25 | 26 | public List sprites = new ArrayList<>(); 27 | 28 | public Sprite(String pictureFile, boolean isSpritesheet, int tileWidth, int tileHeight, int columns, int spacing, int size) { 29 | this.pictureFile = pictureFile; 30 | this.isSpritesheet = isSpritesheet; 31 | this.tileHeight = tileHeight; 32 | this.tileWidth = tileWidth; 33 | this.spacing = spacing; 34 | this.columns = columns; 35 | this.size = size; 36 | 37 | if (AssetPool.hasSprite(new File(pictureFile).getAbsolutePath())) { 38 | System.out.println(AssetPool.getSprite(new File(pictureFile).getAbsolutePath())); 39 | System.out.println("Error trying to recreate a sprite that already exists! Sprite: " + pictureFile); 40 | System.exit(-1); 41 | } 42 | 43 | AssetPool.addSprite(new File(pictureFile).getAbsolutePath(), this); 44 | 45 | try { 46 | this.image = ImageIO.read(new File(pictureFile)); 47 | this.width = image.getWidth(); 48 | this.height = image.getHeight(); 49 | } catch(IOException e) { 50 | e.printStackTrace(); 51 | System.exit(-1); 52 | } 53 | } 54 | 55 | public Sprite(String pictureFile, int width, int height) { 56 | this.pictureFile = pictureFile; 57 | this.width = width; 58 | this.height = height; 59 | this.isSpritesheet = false; 60 | 61 | if (AssetPool.hasSprite(new File(pictureFile).getAbsolutePath())) return; 62 | 63 | AssetPool.addSprite(new File(pictureFile).getAbsolutePath(), this); 64 | try { 65 | this.image = ImageIO.read(new File(pictureFile)); 66 | } catch (IOException e) { 67 | e.printStackTrace(); 68 | System.exit(-1); 69 | } 70 | } 71 | 72 | public void loadSpritesheet() { 73 | int row = 0; 74 | int count = 0; 75 | while (count < this.size) { 76 | for (int column = 0; column < columns; column++) { 77 | int imgX = (column * tileWidth) + (column * spacing); 78 | int imgY = (row * tileHeight) + (row * spacing); 79 | 80 | sprites.add(new SubSprite(this.pictureFile, imgX, imgY, tileWidth, tileHeight, row, column)); 81 | count++; 82 | if (count > this.size - 1) { 83 | break; 84 | } 85 | } 86 | row++; 87 | } 88 | } 89 | 90 | public int getX(int id) { 91 | id -= gid; 92 | id = id % columns; 93 | return id * tileWidth + (id * spacing); 94 | } 95 | 96 | public int getY(int id) { 97 | id -= gid; 98 | id = id / columns; 99 | return id * tileHeight + (id * spacing); 100 | } 101 | 102 | @Override 103 | public Sprite clone() { 104 | return new Sprite(this.pictureFile, this.isSpritesheet, this.tileWidth, this.tileHeight, this.columns, this.spacing, this.size); 105 | } 106 | 107 | @Override 108 | public void draw(Graphics2D g2) { 109 | if (!isSpritesheet) { 110 | g2.drawImage(this.image, (int)parent.transform.position.x, (int)parent.transform.position.y, width, height, null); 111 | } 112 | } 113 | 114 | @Override 115 | public String serialize(int tabSize) { 116 | StringBuilder builder = JString.getBuilder(); 117 | 118 | builder.append(beginObjectProperty("Sprite", tabSize)); 119 | builder.append(addStringProperty("pictureFile", pictureFile, tabSize + 1, true, true)); 120 | builder.append(addBooleanProperty("isSpritesheet", isSpritesheet, tabSize + 1, true, true)); 121 | builder.append(addIntProperty("tileWidth", tileWidth, tabSize + 1, true, true)); 122 | builder.append(addIntProperty("tileHeight", tileHeight, tabSize + 1, true, true)); 123 | builder.append(addIntProperty("columns", columns, tabSize + 1, true, true)); 124 | builder.append(addIntProperty("spacing", spacing, tabSize + 1, true, true)); 125 | builder.append(addIntProperty("width", width, tabSize + 1, true, true)); 126 | builder.append(addIntProperty("height", height, tabSize + 1, true, false)); 127 | builder.append(closeObjectProperty(tabSize)); 128 | 129 | return builder.toString(); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/com/jade/components/SubSprite.java: -------------------------------------------------------------------------------- 1 | package com.jade.components; 2 | 3 | import com.jade.dataStructures.JImage; 4 | import com.jade.dataStructures.JString; 5 | import com.jade.jade.AssetPool; 6 | import com.jade.file.Parser; 7 | 8 | import java.awt.*; 9 | import java.io.File; 10 | 11 | public class SubSprite extends Component { 12 | public Sprite spriteParent; 13 | 14 | public int imgX, imgY, width, height; 15 | public int row, column; 16 | public JImage subImg; 17 | 18 | public SubSprite(String parentImageFile, int imgX, int imgY, int width, int height, int row, int column) { 19 | Sprite parentSprite = AssetPool.getSprite(new File(parentImageFile).getAbsolutePath()); 20 | this.spriteParent = parentSprite; 21 | this.imgX = imgX; 22 | this.imgY = imgY; 23 | this.width = width; 24 | this.height = height; 25 | this.row = row; 26 | this.column = column; 27 | 28 | if (parentSprite != null) { 29 | this.subImg = new JImage(parentSprite.image.getSubimage(imgX, imgY, width, height)); 30 | } 31 | else { 32 | System.out.println("Error! Sprite parent is null: '" + parentImageFile + "'"); 33 | System.exit(-1); 34 | } 35 | } 36 | 37 | public void draw(Graphics2D g2) { 38 | Graphics2D oldGraphics = (Graphics2D)g2.create(); 39 | oldGraphics.translate(parent.transform.position.x, parent.transform.position.y); 40 | 41 | if (parent.getComponent(BoxBounds.class) != null) { 42 | BoxBounds bounds = parent.getComponent(BoxBounds.class); 43 | //oldGraphics.translate(bounds.xBuffer, bounds.yBuffer); 44 | //oldGraphics.rotate(Math.toRadians(bounds.angle), bounds.width / 2.0, bounds.height / 2.0); 45 | oldGraphics.rotate(Math.toRadians(bounds.angle), width / 2.0, height / 2.0); 46 | } else if (parent.getComponent(TriangleBounds.class) != null) { 47 | TriangleBounds bounds = parent.getComponent(TriangleBounds.class); 48 | //oldGraphics.translate(bounds.xBuffer, bounds.yBuffer); 49 | //oldGraphics.rotate(Math.toRadians(bounds.angle), bounds.base / 2.0, bounds.height / 2.0); 50 | oldGraphics.rotate(Math.toRadians(bounds.angle), width / 2.0, height / 2.0); 51 | } 52 | 53 | oldGraphics.drawImage(subImg.image, 0, 0, width, height, null); 54 | oldGraphics.dispose(); 55 | } 56 | 57 | @Override 58 | public SubSprite clone() { 59 | return new SubSprite(spriteParent.pictureFile, imgX, imgY, width, height, row, column); 60 | } 61 | 62 | @Override 63 | public String serialize(int tabSize) { 64 | StringBuilder builder = JString.getBuilder(); 65 | 66 | builder.append(beginObjectProperty("SubSprite", tabSize)); 67 | builder.append(addStringProperty("parentImageFile", spriteParent.pictureFile, tabSize + 1, true, true)); 68 | builder.append(addIntProperty("imgX", imgX, tabSize + 1, true, true)); 69 | builder.append(addIntProperty("imgY", imgY, tabSize + 1, true, true)); 70 | builder.append(addIntProperty("width", width, tabSize + 1, true, true)); 71 | builder.append(addIntProperty("height", height, tabSize + 1, true, true)); 72 | builder.append(addIntProperty("row", row, tabSize + 1, true, true)); 73 | builder.append(addIntProperty("column", column, tabSize + 1, true, false)); 74 | builder.append(closeObjectProperty(tabSize)); 75 | 76 | return builder.toString(); 77 | } 78 | 79 | public static SubSprite deserialize() { 80 | Parser.consumeBeginObjectProperty(); 81 | 82 | String parentImageFile = Parser.consumeStringProperty("parentImageFile"); 83 | Parser.consume(','); 84 | 85 | int imgX = Parser.consumeIntProperty("imgX"); 86 | Parser.consume(','); 87 | int imgY = Parser.consumeIntProperty("imgY"); 88 | Parser.consume(','); 89 | int width = Parser.consumeIntProperty("width"); 90 | Parser.consume(','); 91 | int height = Parser.consumeIntProperty("height"); 92 | Parser.consume(','); 93 | int row = Parser.consumeIntProperty("row"); 94 | Parser.consume(','); 95 | int column = Parser.consumeIntProperty("column"); 96 | Parser.consume('}'); 97 | 98 | return new SubSprite(parentImageFile, imgX, imgY, width, height, row, column); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/com/jade/components/TriangleBounds.java: -------------------------------------------------------------------------------- 1 | package com.jade.components; 2 | 3 | import com.jade.dataStructures.JString; 4 | import com.jade.dataStructures.Vector2; 5 | import com.jade.file.Parser; 6 | import com.jade.jade.GameObject; 7 | import com.jade.main.Constants; 8 | import com.jade.main.LevelScene; 9 | import com.jade.main.Window; 10 | import com.jade.scripts.Player; 11 | 12 | import java.awt.Graphics2D; 13 | import java.awt.BasicStroke; 14 | import java.awt.Color; 15 | 16 | public class TriangleBounds extends Component { 17 | public double base, height, halfWidth, yBuffer, xBuffer; 18 | private double enclosingRadius, halfHeight; 19 | public boolean isPlaying; 20 | public float angle = 0.0f; 21 | 22 | private float x1, x2, x3, y1, y2, y3; 23 | 24 | // For polygon intersection tests 25 | private final int INSIDE = 0; 26 | private final int LEFT = 1; 27 | private final int RIGHT = 2; 28 | private final int BOTTOM = 4; 29 | private final int TOP = 8; 30 | 31 | public boolean isSelected = false; 32 | 33 | public TriangleBounds(double base, double height, float angle, boolean isPlaying) { 34 | this.base = base; 35 | this.height = height; 36 | this.isPlaying = isPlaying; 37 | this.angle = angle; 38 | this.halfWidth = base / 2.0; 39 | this.yBuffer = Constants.GRID_HEIGHT - height; 40 | this.xBuffer = (Constants.GRID_WIDTH - base) / 2.0; 41 | this.halfHeight = height / 2.0; 42 | this.enclosingRadius = Math.max(halfHeight, halfWidth); 43 | } 44 | 45 | public TriangleBounds(double base, double height, boolean isPlaying) { 46 | this.base = base; 47 | this.height = height; 48 | this.isPlaying = isPlaying; 49 | this.halfWidth = base / 2.0; 50 | this.yBuffer = Constants.GRID_HEIGHT - height; 51 | this.xBuffer = (Constants.GRID_WIDTH - base) / 2.0; 52 | this.halfHeight = height / 2.0; 53 | this.enclosingRadius = Math.max(halfHeight, halfWidth); 54 | } 55 | 56 | public TriangleBounds clone() { 57 | return new TriangleBounds(base, height, isPlaying); 58 | } 59 | 60 | @Override 61 | public void start() { 62 | recalculateTransform(); 63 | } 64 | 65 | @Override 66 | public void update(double dt) { 67 | recalculateTransform(); 68 | if (Window.isEditing) return; 69 | 70 | if (broadPhase(LevelScene.getScene().player)) { 71 | // Possible Collision! 72 | if (narrowPhase(LevelScene.getScene().player)) { 73 | // There is definitely a collision! 74 | LevelScene.getScene().player.getComponent(Player.class).die(); 75 | } 76 | } 77 | } 78 | 79 | private boolean broadPhase(GameObject plr) { 80 | double centerX = x1; 81 | double centerY = y1 + halfHeight; 82 | if (angle % 360 == 0) { 83 | // Don't change anything. 84 | } else if (angle % 270 == 0) { 85 | centerX = x1 + halfWidth; 86 | centerY = y1; 87 | } else if (angle % 180 == 0 && angle % 360 != 0) { 88 | centerY = y1 - halfHeight; 89 | } else if (angle % 90 == 0 && angle % 360 != 0) { 90 | centerX = x1 - halfWidth; 91 | centerY = y1; 92 | } 93 | 94 | double plrCenterX = plr.transform.position.x + plr.getComponent(BoxBounds.class).halfWidth; 95 | double plrCenterY = plr.transform.position.y + plr.getComponent(BoxBounds.class).halfHeight; 96 | 97 | return ((plrCenterX - centerX) * (plrCenterX - centerX)) + ((plrCenterY - centerY) * (plrCenterY - centerY)) <= 98 | (this.enclosingRadius + plr.getComponent(BoxBounds.class).halfWidth) * (this.enclosingRadius + plr.getComponent(BoxBounds.class).halfWidth); 99 | } 100 | 101 | private boolean narrowPhase(GameObject plr) { 102 | Vector2 p1 = new Vector2(x1, y1); 103 | Vector2 p2 = new Vector2(x2, y2); 104 | Vector2 p3 = new Vector2(x3, y3); 105 | 106 | BoxBounds bounds = plr.getComponent(BoxBounds.class); 107 | Vector2 origin = new Vector2(bounds.parent.transform.position.x + bounds.halfWidth, bounds.parent.transform.position.y + bounds.halfHeight); 108 | 109 | return (playerIntersectingLine(p1, p2, origin, -bounds.angle, 0, bounds, plr.transform.position)) || 110 | (playerIntersectingLine(p1, p3, origin, -bounds.angle, 0, bounds, plr.transform.position)) || 111 | (playerIntersectingLine(p2, p3, origin, -bounds.angle, 0, bounds, plr.transform.position)); 112 | } 113 | 114 | public boolean isColliding(BoxBounds bounds, Vector2 pos) { 115 | Vector2 p1 = new Vector2(x1, y1); 116 | Vector2 p2 = new Vector2(x2, y2); 117 | Vector2 p3 = new Vector2(x3, y3); 118 | 119 | Vector2 origin = new Vector2(pos.x + bounds.halfWidth, pos.y + bounds.halfHeight); 120 | 121 | return (playerIntersectingLine(p1, p2, origin, -bounds.angle, 0, bounds, pos)) || 122 | (playerIntersectingLine(p1, p3, origin, -bounds.angle, 0, bounds, pos)) || 123 | (playerIntersectingLine(p2, p3, origin, -bounds.angle, 0, bounds, pos)); 124 | } 125 | 126 | 127 | private boolean playerIntersectingLine(Vector2 oldP1, Vector2 oldP2, Vector2 origin, double angle, int depth, BoxBounds bounds, Vector2 playerPos) { 128 | if (depth > 5) return true; 129 | Vector2 p1 = rotateAbout(Math.toRadians(angle), oldP1, origin); 130 | Vector2 p2 = rotateAbout(Math.toRadians(angle), oldP2, origin); 131 | 132 | int code1 = computeRegionCode(p1, bounds, playerPos); 133 | int code2 = computeRegionCode(p2, bounds, playerPos); 134 | 135 | if (code1 == 0 && code2 == 0) { 136 | // Line is completely inside 137 | return true; 138 | } else if ((code1 & code2) != 0) { 139 | // Line is completely outside 140 | return false; 141 | } else { 142 | int ymax = (int)(playerPos.y + bounds.height); 143 | int ymin = (int)(playerPos.y); 144 | int xmax = (int)(playerPos.x + bounds.width); 145 | int xmin = (int)(playerPos.x); 146 | 147 | int codeForPointOutside; 148 | Vector2 newVec = new Vector2(); 149 | if (code1 != 0) 150 | codeForPointOutside = code1; 151 | else 152 | codeForPointOutside = code2; 153 | if ((codeForPointOutside & TOP) == TOP) { 154 | newVec.x = p1.x + (p2.x - p1.x) * (ymin - p1.y) / (p2.y - p1.y); 155 | newVec.y = ymin; 156 | } else if ((codeForPointOutside & BOTTOM) == BOTTOM) { 157 | newVec.x = p1.x + (p2.x - p1.x) * (ymax - p1.y) / (p2.y - p1.y); 158 | newVec.y = ymax; 159 | } else if ((codeForPointOutside & RIGHT) == RIGHT) { 160 | if (p2.x - p1.x != 0) { 161 | newVec.y = p1.y + (p2.y - p1.y) * (xmax - p1.x) / (p2.x - p1.x); 162 | newVec.x = xmax; 163 | } else { 164 | newVec.y = p1.y + (p2.y - p1.y); 165 | newVec.x = p1.x; 166 | } 167 | } else if ((codeForPointOutside & LEFT) == LEFT) { 168 | if (p2.x - p1.x != 0) { 169 | newVec.y = p1.y + (p2.y - p1.y) * (xmin - p1.x) / (p2.x - p1.x); 170 | newVec.x = xmin; 171 | } else { 172 | newVec.y = p1.y + (p2.y - p1.y); 173 | newVec.x = p1.x; 174 | } 175 | } 176 | 177 | if (codeForPointOutside == code1) { 178 | Vector2 newP1 = rotateAbout(Math.toRadians(0), newVec, origin); 179 | return playerIntersectingLine(newP1, oldP2, origin, angle, depth + 1, bounds, playerPos); 180 | } else { 181 | Vector2 newP2 = rotateAbout(Math.toRadians(0), newVec, origin); 182 | return playerIntersectingLine(oldP1, newP2, origin, angle, depth + 1, bounds, playerPos); 183 | } 184 | } 185 | } 186 | 187 | private int computeRegionCode(Vector2 point, BoxBounds bounds, Vector2 playerPos) { 188 | int code = INSIDE; 189 | Vector2 topLeftPlr = playerPos; 190 | 191 | if (point.x < topLeftPlr.x) 192 | code |= LEFT; 193 | else if (point.x > topLeftPlr.x + bounds.width) 194 | code |= RIGHT; 195 | if (point.y < topLeftPlr.y) 196 | code |= TOP; 197 | else if (point.y > topLeftPlr.y + bounds.height) 198 | code |= BOTTOM; 199 | 200 | return code; 201 | } 202 | 203 | public boolean pointInTriangle(float x, float y) { 204 | float v0x = x3 - x1; 205 | float v0y = y3 - y1; 206 | float v1x = x2 - x1; 207 | float v1y = y2 - y1; 208 | float v2x = x - x1; 209 | float v2y = y - y1; 210 | 211 | float dot00 = dot(v0x, v0y, v0x, v0y); 212 | float dot01 = dot(v0x, v0y, v1x, v1y); 213 | float dot02 = dot(v0x, v0y, v2x, v2y); 214 | float dot11 = dot(v1x, v1y, v1x, v1y); 215 | float dot12 = dot(v1x, v1y, v2x, v2y); 216 | 217 | float invDenom = 1 / (dot00 * dot11 - dot01 * dot01); 218 | float u = (dot11 * dot02 - dot01 * dot12) * invDenom; 219 | float v = (dot00 * dot12 - dot01 * dot02) * invDenom; 220 | 221 | return (u >= 0) && (v >= 0) && (u + v < 1); 222 | } 223 | 224 | public boolean isContainedInRectangle(float x, float y, float w, float h) { 225 | return this.parent.transform.position.x + xBuffer >= x && this.parent.transform.position.x + xBuffer + base <= x + w && 226 | this.parent.transform.position.y + yBuffer >= y && this.parent.transform.position.y + yBuffer + height <= y + h; 227 | } 228 | 229 | private float dot(float x0, float y0, float x1, float y1) { 230 | return (x0 * x1) + (y0 * y1); 231 | } 232 | 233 | private void recalculatePoints() { 234 | double rAngle = Math.toRadians(angle); 235 | Vector2 p1 = new Vector2(x1, y1); 236 | Vector2 p2 = new Vector2(x2, y2); 237 | Vector2 p3 = new Vector2(x3, y3); 238 | Vector2 origin = new Vector2(parent.transform.position.x + (Constants.GRID_WIDTH / 2.0f), parent.transform.position.y + (Constants.GRID_HEIGHT / 2.0f)); 239 | 240 | p1 = rotateAbout(rAngle, p1, origin); 241 | p2 = rotateAbout(rAngle, p2, origin); 242 | p3 = rotateAbout(rAngle, p3, origin); 243 | 244 | x1 = p1.x; 245 | y1 = p1.y; 246 | x2 = p2.x; 247 | y2 = p2.y; 248 | x3 = p3.x; 249 | y3 = p3.y; 250 | } 251 | 252 | private Vector2 rotateAbout(double angle, Vector2 p, Vector2 o) { 253 | double cos = Math.round(Math.cos(angle) * 100.0) / 100.0; 254 | double sin = Math.round(Math.sin(angle) * 100.0) / 100.0; 255 | Vector2 newVector = new Vector2(p.x, p.y); 256 | newVector.x -= o.x; 257 | newVector.y -= o.y; 258 | 259 | float newX = (float)((newVector.x * cos) - (newVector.y * sin)); 260 | float newY = (float)((newVector.x * sin) + (newVector.y * cos)); 261 | 262 | return new Vector2(newX + o.x, newY + o.y); 263 | } 264 | 265 | private void recalculateTransform() { 266 | x1 = (float)(xBuffer + parent.transform.position.x + halfWidth); 267 | y1 = (float)(yBuffer + parent.transform.position.y); 268 | x2 = (float)(xBuffer + parent.transform.position.x); 269 | y2 = (float)(yBuffer + parent.transform.position.y + height); 270 | x3 = (float)(xBuffer + parent.transform.position.x + base); 271 | y3 = y2; 272 | 273 | recalculatePoints(); 274 | } 275 | 276 | @Override 277 | public void draw(Graphics2D g2) { 278 | if (isSelected) { 279 | Graphics2D oldGraphics = (Graphics2D)g2.create(); 280 | oldGraphics.translate(parent.transform.position.x, parent.transform.position.y); 281 | oldGraphics.rotate(Math.toRadians(angle), Constants.TILE_WIDTH / 2.0, Constants.TILE_WIDTH / 2.0); 282 | oldGraphics.setStroke(new BasicStroke(2)); 283 | oldGraphics.setColor(Color.GREEN); 284 | oldGraphics.drawLine((int)(halfWidth + xBuffer), (int)yBuffer, (int)xBuffer, (int)(height + yBuffer)); 285 | oldGraphics.drawLine((int)(halfWidth + xBuffer), (int)yBuffer, (int)(base + xBuffer), (int)(height + yBuffer)); 286 | oldGraphics.drawLine((int)xBuffer, (int)(height + yBuffer), (int)(base + xBuffer), (int)(height + yBuffer)); 287 | } 288 | } 289 | 290 | @Override 291 | public String serialize(int tabSize) { 292 | StringBuilder builder = JString.getBuilder(); 293 | 294 | builder.append(beginObjectProperty("TriangleBounds", tabSize)); 295 | builder.append(addDoubleProperty("base", base, tabSize + 1, true, true)); 296 | builder.append(addDoubleProperty("height", height, tabSize + 1, true, true)); 297 | builder.append(addFloatProperty("angle", angle, tabSize + 1, true, true)); 298 | builder.append(addBooleanProperty("isPlaying", isPlaying, tabSize + 1, true, false)); 299 | builder.append(closeObjectProperty(tabSize)); 300 | 301 | return builder.toString(); 302 | } 303 | 304 | public static TriangleBounds deserialize() { 305 | Parser.consumeBeginObjectProperty(); 306 | 307 | double base = Parser.consumeDoubleProperty("base"); 308 | Parser.consume(','); 309 | double height = Parser.consumeDoubleProperty("height"); 310 | Parser.consume(','); 311 | float angle = Parser.consumeFloatProperty("angle"); 312 | Parser.consume(','); 313 | boolean isPlaying = Parser.consumeBooleanProperty("isPlaying"); 314 | Parser.consume('}'); 315 | 316 | return new TriangleBounds(base, height, angle, isPlaying); 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /src/com/jade/dataStructures/ConnectionGene.java: -------------------------------------------------------------------------------- 1 | package com.jade.dataStructures; 2 | 3 | import com.jade.components.Component; 4 | 5 | import java.awt.*; 6 | 7 | public class ConnectionGene extends Component implements Comparable { 8 | 9 | private NodeGene inNode; 10 | private NodeGene outNode; 11 | private int inNodeIndex; 12 | private int outNodeIndex; 13 | private float weight; 14 | private boolean expressed; 15 | private int innovation; 16 | private boolean debug = true; 17 | 18 | public ConnectionGene(NodeGene inNode, NodeGene outNode, float weight, boolean expressed, InnovationGenerator innovationGenerator) { 19 | this.inNode = inNode; 20 | this.outNode = outNode; 21 | this.weight = weight; 22 | this.expressed = expressed; 23 | this.inNodeIndex = inNode.getId(); 24 | this.outNodeIndex = outNode.getId(); 25 | 26 | this.innovation = innovationGenerator.getInnovation(this); 27 | } 28 | 29 | public ConnectionGene(int inNode, int outNode, float weight, boolean expressed, int innovation) { 30 | this.inNodeIndex = inNode; 31 | this.outNodeIndex = outNode; 32 | this.weight = weight; 33 | this.expressed = expressed; 34 | this.innovation = innovation; 35 | } 36 | 37 | public ConnectionGene(ConnectionGene toBeCopied) { 38 | inNodeIndex = toBeCopied.inNodeIndex; 39 | outNodeIndex = toBeCopied.outNodeIndex; 40 | weight = toBeCopied.weight; 41 | innovation = toBeCopied.innovation; 42 | expressed = toBeCopied.expressed; 43 | } 44 | 45 | public void setNodes(NodeGene inNode, NodeGene outNode) { 46 | this.inNode = inNode; 47 | this.outNode = outNode; 48 | } 49 | 50 | public void feedForward() { 51 | this.outNode.addValue(this.inNode.getValue() * this.weight); 52 | this.outNode.setValue(sigmoid(this.outNode.getValue())); 53 | } 54 | private float sigmoid(float x) { 55 | return (float)(1 / (1 + Math.exp(-1.9 * x))); 56 | } 57 | 58 | public int getInNodeIndex() { return this.inNodeIndex; } 59 | public int getOutNodeIndex() { return this.outNodeIndex; } 60 | 61 | public NodeGene getInNode() { 62 | return this.inNode; 63 | } 64 | 65 | public NodeGene getOutNode() { 66 | return this.outNode; 67 | } 68 | 69 | public float getWeight() { 70 | return this.weight; 71 | } 72 | 73 | public void disable() { 74 | this.expressed = false; 75 | } 76 | 77 | public void enable() { 78 | this.expressed = true; 79 | } 80 | 81 | public boolean isExpressed() { 82 | return this.expressed; 83 | } 84 | 85 | public void setInnovation(int innovation) { 86 | this.innovation = innovation; 87 | } 88 | 89 | public int getInnovation() { 90 | return this.innovation; 91 | } 92 | 93 | public void setWeight(float weight) { 94 | this.weight = weight; 95 | } 96 | 97 | public ConnectionGene copy() { 98 | return new ConnectionGene(inNodeIndex, outNodeIndex, weight, expressed, innovation); 99 | } 100 | 101 | @Override 102 | public Component clone() { 103 | return copy(); 104 | } 105 | 106 | @Override 107 | public void draw(Graphics2D g2) { 108 | if (!expressed) 109 | return; 110 | else 111 | g2.setColor(Color.GREEN); 112 | 113 | if (debug) { 114 | g2.drawLine((int) inNode.x + 40, (int) inNode.y + 20, (int) outNode.x, (int) outNode.y + 20); 115 | g2.fillRect((int) outNode.x - 10, (int) outNode.y + 20, 10, 10); 116 | 117 | float centerX = inNode.x + Math.abs((outNode.x - inNode.x) / 2.0f); 118 | float centerY; 119 | if (outNode.y - inNode.y > 0) 120 | centerY = inNode.y + Math.abs((outNode.y - inNode.y) / 2.0f); 121 | else 122 | centerY = inNode.y - Math.abs((outNode.y - inNode.y) / 2.0f); 123 | g2.setFont(new Font("Times New Roman", Font.PLAIN, 12)); 124 | g2.setColor(Color.WHITE); 125 | g2.drawString("" + weight, centerX, centerY); 126 | g2.drawString("" + innovation, centerX, centerY + 10); 127 | } else { 128 | g2.drawLine((int)inNode.x + 5, (int)inNode.y + 5, (int)outNode.x, (int)outNode.y + 5); 129 | } 130 | } 131 | 132 | @Override 133 | public int compareTo(ConnectionGene connectionGene) { 134 | if (connectionGene.getInnovation() == this.innovation) return 0; 135 | 136 | return this.innovation > connectionGene.getInnovation() ? 1 : -1; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/com/jade/dataStructures/Genome.java: -------------------------------------------------------------------------------- 1 | package com.jade.dataStructures; 2 | 3 | import com.jade.components.Component; 4 | 5 | import java.awt.*; 6 | import java.util.*; 7 | import java.util.List; 8 | 9 | public class Genome extends Component implements Comparable, Runnable { 10 | private List connections; 11 | private List genes; 12 | private final float PROBABILITY_PERTURBING = 0.9f; 13 | private final float PROBABILITY_MUTATING = 0.8f; 14 | private final float MAX_PERTURBATION = 0.1f; 15 | private final float PROBABILITY_ADD_NEW_NODE = 0.03f; 16 | private final float PROBABILITY_ADD_CONNECTION = 0.05f; 17 | private static Random random = new Random(); 18 | private int x, y; 19 | public int globalRank; 20 | private float fitness, adjustedFitness; 21 | private boolean debug = true; 22 | 23 | public boolean isReady = true; 24 | public List outputs = new ArrayList<>(); 25 | 26 | public Genome(int x, int y) { 27 | this.connections = new ArrayList<>(); 28 | this.genes = new ArrayList<>(); 29 | this.x = x; 30 | this.y = y; 31 | this.fitness = 0.0f; 32 | this.adjustedFitness = 0.0f; 33 | this.globalRank = 0; 34 | } 35 | 36 | public Genome(int x, int y, float fitness, float adjustedFitness, int globalRank) { 37 | this.connections = new ArrayList<>(); 38 | this.genes = new ArrayList<>(); 39 | this.x = x; 40 | this.y = y; 41 | this.fitness = fitness; 42 | this.adjustedFitness = adjustedFitness; 43 | this.globalRank = globalRank; 44 | } 45 | 46 | public static Genome basicGenome(List inputs, List output, InnovationGenerator innovationGenerator) { 47 | Genome genome = new Genome(0, 0); 48 | for (NodeGene gene : output) { 49 | genome.addNodeGene(gene); 50 | } 51 | 52 | for (NodeGene gene : inputs) { 53 | genome.addNodeGene(gene); 54 | for (NodeGene o : output) { 55 | genome.addConnectionGene(new ConnectionGene(gene, o, 0.0f, true, innovationGenerator)); 56 | } 57 | } 58 | genome.mutate(innovationGenerator); 59 | 60 | return genome; 61 | } 62 | 63 | public Genome copy() { 64 | Genome newGene = new Genome(this.x, this.y, this.fitness, this.adjustedFitness, this.globalRank); 65 | for (NodeGene nodeGene : genes) { 66 | NodeGene newNodeGene = nodeGene.copy(); 67 | newGene.addNodeGene(newNodeGene); 68 | } 69 | 70 | for (ConnectionGene con : connections) { 71 | ConnectionGene newCon = con.copy(); 72 | newCon.setNodes(newGene.getNodeOfId(newCon.getInNodeIndex()), newGene.getNodeOfId(newCon.getOutNodeIndex())); 73 | newGene.addConnectionGene(newCon); 74 | } 75 | 76 | return newGene; 77 | } 78 | 79 | public void mutate(InnovationGenerator innovationGenerator) { 80 | for (ConnectionGene con : connections) { 81 | if (random.nextFloat() < PROBABILITY_MUTATING) { // 80% chance of being mutated 82 | if (random.nextFloat() < PROBABILITY_PERTURBING) { // 90% chance of being perturbed 83 | con.setWeight(con.getWeight() + ((random.nextFloat() * 2 - 1f) * MAX_PERTURBATION)); 84 | if (con.getWeight() >= 1.0f) con.setWeight(1.0f); 85 | else if (con.getWeight() <= 0.0f) con.setWeight(0.0f); 86 | } else { // 10% chance of random new weight 87 | con.setWeight(random.nextFloat()); 88 | } 89 | } 90 | } 91 | 92 | if (random.nextFloat() < PROBABILITY_ADD_NEW_NODE) { 93 | addNode(innovationGenerator); 94 | } 95 | 96 | if (random.nextFloat() < PROBABILITY_ADD_CONNECTION) { 97 | addConnection(innovationGenerator); 98 | } 99 | } 100 | 101 | public void addNode(InnovationGenerator innovation) { 102 | ConnectionGene conToSplit = connections.get(random.nextInt(connections.size())); 103 | 104 | NodeGene newGene = new NodeGene(NodeGene.Type.HIDDEN, genes.size(), 0.0f, conToSplit.getInNode().layer + 1); 105 | genes.add(newGene); 106 | 107 | if (conToSplit.getOutNode().layer - conToSplit.getInNode().layer < 2) { 108 | Stack genesToVisit = new Stack<>(); 109 | List visited = new ArrayList<>(); 110 | visited.add(conToSplit.getOutNode()); 111 | genesToVisit.push(conToSplit.getOutNode()); 112 | while (genesToVisit.size() > 0) { 113 | NodeGene current = genesToVisit.pop(); 114 | current.layer++; 115 | 116 | for (ConnectionGene con : connections) { 117 | if (con.getInNode() == current && !visited.contains(current)) { 118 | visited.add(current); 119 | genesToVisit.push(con.getInNode()); 120 | } 121 | } 122 | } 123 | } 124 | conToSplit.disable(); 125 | 126 | connections.add(new ConnectionGene(conToSplit.getInNode(), newGene, 1.0f, true, innovation)); 127 | connections.add(new ConnectionGene(newGene, conToSplit.getOutNode(), conToSplit.getWeight(), true, innovation)); 128 | } 129 | 130 | public void addConnection(InnovationGenerator innovation) { 131 | boolean added = false; 132 | int numTries = 0; 133 | do { 134 | numTries++; 135 | NodeGene newInGene = genes.get(random.nextInt(genes.size())); 136 | NodeGene newOutGene; 137 | int otherNumTries = 0; 138 | do { 139 | otherNumTries++; 140 | newOutGene = genes.get(random.nextInt(genes.size())); 141 | } while (newOutGene == newInGene && otherNumTries < 5); 142 | if (otherNumTries >= 5) continue; 143 | 144 | boolean exists = false; 145 | boolean canConnect = true; 146 | for (ConnectionGene con : connections) { 147 | if ((newInGene == con.getInNode() || newInGene == con.getOutNode()) && (newOutGene == con.getInNode() || newOutGene == con.getOutNode())) { 148 | exists = true; 149 | break; 150 | } 151 | if (newInGene.getType() == newOutGene.getType() && (newInGene.getType() == NodeGene.Type.INPUT || newInGene.getType() == NodeGene.Type.OUTPUT)) { 152 | canConnect = false; 153 | break; 154 | } 155 | if ((newInGene.getType() == NodeGene.Type.OUTPUT && newOutGene.getType() == NodeGene.Type.INPUT) || (newInGene.getType() == NodeGene.Type.INPUT && newOutGene.getType() == NodeGene.Type.OUTPUT)) { 156 | canConnect = false; 157 | break; 158 | } 159 | } 160 | if (exists || !canConnect) continue; 161 | 162 | if (newInGene.layer > newOutGene.layer) { 163 | NodeGene tmp = newInGene; 164 | newInGene = newOutGene; 165 | newOutGene = tmp; 166 | } 167 | 168 | ConnectionGene newCon = new ConnectionGene(newInGene, newOutGene, random.nextFloat(), true, innovation); 169 | connections.add(newCon); 170 | added = true; 171 | } while (!added && numTries < 3); 172 | } 173 | 174 | public void evaluate() { 175 | isReady = false; 176 | List outputs = new ArrayList<>(); 177 | Collections.sort(genes); 178 | 179 | for (int i=0; i < genes.size(); i++) { 180 | NodeGene gene = genes.get(i); 181 | for (ConnectionGene con : connections) { 182 | if (con.isExpressed() && con.getInNode() == gene) { 183 | con.feedForward(); 184 | } 185 | } 186 | 187 | if (gene.getType() == NodeGene.Type.OUTPUT) { 188 | outputs.add(gene); 189 | } 190 | } 191 | 192 | isReady = true; 193 | this.outputs = outputs; 194 | } 195 | 196 | public static int hasInnovationIsExpressed(Genome genome, int innovation) { 197 | for (ConnectionGene con : genome.getConnectionGenes()) { 198 | if (con.getInnovation() == innovation) { 199 | if (con.isExpressed()) { 200 | return 2; 201 | } else { 202 | return 1; 203 | } 204 | } 205 | } 206 | return 0; 207 | } 208 | 209 | public static ConnectionGene getInnovation(Genome genome, int innovation) { 210 | for (ConnectionGene con : genome.getConnectionGenes()) { 211 | if (con.getInnovation() == innovation) { 212 | return con; 213 | } 214 | } 215 | return null; 216 | } 217 | 218 | public boolean hasNodeOfId(int id) { 219 | for (NodeGene node : genes) { 220 | if (node.getId() == id) { 221 | return true; 222 | } 223 | } 224 | return false; 225 | } 226 | 227 | public NodeGene getNodeOfId(int id) { 228 | for (NodeGene node : genes) { 229 | if (node.getId() == id) { 230 | return node; 231 | } 232 | } 233 | return null; 234 | } 235 | 236 | public ConnectionGene addAndWireConnectionGene(ConnectionGene connection) { 237 | if (!hasNodeOfId(connection.getInNode().getId())) addNodeGene(connection.getInNode().copy()); 238 | if (!hasNodeOfId(connection.getOutNode().getId())) addNodeGene(connection.getOutNode().copy()); 239 | 240 | ConnectionGene newCon = connection.copy(); 241 | newCon.setNodes(getNodeOfId(connection.getInNode().getId()), getNodeOfId(connection.getOutNode().getId())); 242 | addConnectionGene(newCon); 243 | return newCon; 244 | } 245 | 246 | public static Genome makeBaby(Genome genome1, Genome genome2) { 247 | Genome baby = new Genome(0, 0); 248 | List allGenomes = new ArrayList<>(genome1.getConnectionGenes()); 249 | List allNodes = new ArrayList<>(genome1.getNodeGenes()); 250 | for (ConnectionGene gene : genome2.getConnectionGenes()) { 251 | if (!allGenomes.contains(gene)) { 252 | allGenomes.add(gene); 253 | } 254 | } 255 | for (NodeGene gene : genome2.getNodeGenes()) { 256 | if (!allNodes.contains(gene)) { 257 | allNodes.add(gene); 258 | } 259 | } 260 | 261 | for (ConnectionGene con : allGenomes) { 262 | int gene1HasExpressed = hasInnovationIsExpressed(genome1, con.getInnovation()); 263 | int gene2HasExpressed = hasInnovationIsExpressed(genome2, con.getInnovation()); 264 | boolean bothHave = gene1HasExpressed > 0 && gene2HasExpressed > 0; 265 | boolean bothDisabled = gene1HasExpressed == 1 && gene2HasExpressed == 1; 266 | boolean bothEnabled = gene1HasExpressed == 2 && gene2HasExpressed == 2; 267 | 268 | if (bothHave && bothEnabled) { // 100% chance to add it 269 | baby.addAndWireConnectionGene(con); 270 | } else if (bothHave && bothDisabled) { // 100% chance to add it, 100% chance it is disabled 271 | ConnectionGene newCon = baby.addAndWireConnectionGene(con); 272 | newCon.disable(); 273 | } else if (bothHave) { // One is enabled, one is disabled 274 | ConnectionGene newCon = baby.addAndWireConnectionGene(con); 275 | if (Genome.random.nextFloat() > 0.75) { 276 | newCon.disable(); 277 | } else { 278 | newCon.enable(); 279 | } 280 | } else { 281 | if (genome1.getFitness() == genome2.getFitness()) { // If they have the same fitness, randomly assign excess/disjoint gene to child 282 | if (Genome.random.nextBoolean()) { 283 | baby.addAndWireConnectionGene(con); 284 | } 285 | } else if (genome1.getFitness() > genome2.getFitness()) { // If gene1 has this connection, and is stronger, then add conection to child 286 | if (gene1HasExpressed > 0) { // Gene1 was part of the stronger parent 287 | baby.addAndWireConnectionGene(con); 288 | } 289 | } else if (genome1.getFitness() < genome2.getFitness()) { // If gene2 has this connection, and is stronger, then add connection to child 290 | if (gene2HasExpressed > 0) { // Gene2 was part of the stronger parent 291 | baby.addAndWireConnectionGene(con); 292 | } 293 | } 294 | } 295 | } 296 | 297 | return baby; 298 | } 299 | 300 | public static float compatibilityDistance(Genome genome1, Genome genome2, float c1, float c2, float c3) { 301 | int excessGenes = countExcessGenes(genome1, genome2); 302 | int disjointGenes = countDisjointGenes(genome1, genome2); 303 | float weightDifferences = averageWeightDifferences(genome1, genome2); 304 | 305 | int highestNumberOfGenes = Math.max(genome1.getNodeGenes().size(), genome2.getNodeGenes().size()); 306 | if (highestNumberOfGenes < 20) { 307 | highestNumberOfGenes = 1; 308 | } 309 | 310 | return ((c1 * excessGenes) / highestNumberOfGenes) + ((c2 * disjointGenes) / highestNumberOfGenes) + 311 | (c3 * weightDifferences); 312 | } 313 | 314 | public static float averageWeightDifferences(Genome genome1, Genome genome2) { 315 | float weightDifferences = 0.0f; 316 | int matchingGenes = 0; 317 | 318 | List allGenomes = new ArrayList<>(genome1.getConnectionGenes()); 319 | List allNodes = new ArrayList<>(genome1.getNodeGenes()); 320 | for (ConnectionGene gene : genome2.getConnectionGenes()) { 321 | if (!allGenomes.contains(gene)) { 322 | allGenomes.add(gene); 323 | } 324 | } 325 | for (NodeGene gene : genome2.getNodeGenes()) { 326 | if (!allNodes.contains(gene)) { 327 | allNodes.add(gene); 328 | } 329 | } 330 | 331 | for (ConnectionGene con : allGenomes) { 332 | ConnectionGene con1 = getInnovation(genome1, con.getInnovation()); 333 | ConnectionGene con2 = getInnovation(genome2, con.getInnovation()); 334 | 335 | if (con1 != null && con2 != null && con1.getInnovation() == con2.getInnovation()) { 336 | float difference = Math.abs(con1.getWeight() - con2.getWeight()); 337 | weightDifferences += difference; 338 | matchingGenes++; 339 | } 340 | } 341 | 342 | if (matchingGenes == 0) { 343 | return 0; 344 | } else { 345 | return weightDifferences / (float)matchingGenes; 346 | } 347 | } 348 | 349 | public static int countDisjointGenes(Genome genome1, Genome genome2) { 350 | List genome1List = genome1.getConnectionGenes(); 351 | List genome2List = genome2.getConnectionGenes(); 352 | Collections.sort(genome1List); 353 | Collections.sort(genome2List); 354 | Set genome1Set = new HashSet<>(); 355 | Set genome2Set = new HashSet<>(); 356 | 357 | for (ConnectionGene con : genome1List) { 358 | genome1Set.add(con.getInnovation()); 359 | } 360 | for (ConnectionGene con : genome2List) { 361 | genome2Set.add(con.getInnovation()); 362 | } 363 | 364 | int genome1LastInno = genome1List.get(genome1List.size() - 1).getInnovation(); 365 | int genome2LastInno = genome2List.get(genome2List.size() - 1).getInnovation(); 366 | int excess = Math.abs(genome1LastInno - genome2LastInno); 367 | int highestInno = Math.max(genome1LastInno, genome2LastInno) + 1; 368 | 369 | genome1Set.retainAll(genome2Set); 370 | return Math.max(highestInno - genome1Set.size() - excess, 0); 371 | } 372 | 373 | public static int countExcessGenes(Genome genome1, Genome genome2) { 374 | List genome1List = genome1.getConnectionGenes(); 375 | List genome2List = genome2.getConnectionGenes(); 376 | Collections.sort(genome1List); 377 | Collections.sort(genome2List); 378 | int genome1LastInno = genome1List.get(genome1List.size() - 1).getInnovation(); 379 | int genome2LastInno = genome2List.get(genome2List.size() - 1).getInnovation(); 380 | return Math.abs(genome1LastInno - genome2LastInno); 381 | } 382 | 383 | @Override 384 | public Component clone() { 385 | return null; 386 | } 387 | 388 | @Override 389 | public void draw(Graphics2D g2) { 390 | Collections.sort(genes); 391 | 392 | if (debug) { 393 | int currentLayer = 0; 394 | int currentY = 100; 395 | for (int i = 0; i < genes.size(); i++) { 396 | if (currentLayer != genes.get(i).layer) { 397 | currentLayer = genes.get(i).layer; 398 | if (currentLayer % 2 != 0) 399 | currentY = this.y + 100 + (currentLayer * 20); 400 | else 401 | currentY = this.y + 100; 402 | } 403 | 404 | genes.get(i).x = this.x + (currentLayer * 150) + 50; 405 | genes.get(i).y = currentY; 406 | currentY += 100; 407 | } 408 | 409 | for (ConnectionGene con : connections) { 410 | if (!con.isExpressed()) continue; 411 | con.draw(g2); 412 | } 413 | 414 | for (NodeGene gene : genes) { 415 | gene.draw(g2); 416 | } 417 | } else { 418 | int currentLayer = 0; 419 | int currentY = 300; 420 | for (int i = 0; i < genes.size(); i++) { 421 | if (currentLayer != genes.get(i).layer) { 422 | currentLayer = genes.get(i).layer; 423 | if (currentLayer % 2 != 0) 424 | currentY = this.y + 300 + (currentLayer * 20); 425 | else 426 | currentY = this.y + 300; 427 | } 428 | genes.get(i).x = this.x + (currentLayer * 20) + 200; 429 | genes.get(i).y = currentY; 430 | currentY += 20; 431 | } 432 | } 433 | } 434 | 435 | public List getInputs() { 436 | List inputs = new ArrayList<>(); 437 | Collections.sort(genes); 438 | 439 | for (NodeGene gene : genes) { 440 | if (gene.getType() == NodeGene.Type.INPUT) { 441 | inputs.add(gene); 442 | } 443 | } 444 | 445 | return inputs; 446 | } 447 | 448 | public void addNodeGene(NodeGene gene) { genes.add(gene); } 449 | public void addConnectionGene(ConnectionGene connection) { connections.add(connection); } 450 | public List getNodeGenes() { return genes; } 451 | public List getConnectionGenes() { return connections; } 452 | public float getFitness() { return this.fitness; } 453 | public void setFitness(float val) { this.fitness = val; } 454 | public void printOrderedByInnovation() { 455 | Collections.sort(connections); 456 | for (ConnectionGene con : connections) { 457 | System.out.printf("========="); 458 | } 459 | System.out.println(); 460 | for (ConnectionGene con : connections) { 461 | System.out.printf("|%4d |", con.getInnovation()); 462 | } 463 | System.out.println(); 464 | for (ConnectionGene con : connections) { 465 | System.out.printf("|%2d → %-2d|", con.getInNode().getId() + 1, con.getOutNode().getId() + 1); 466 | } 467 | System.out.println(); 468 | for (ConnectionGene con : connections) { 469 | if (!con.isExpressed()) { 470 | System.out.printf("| %-6s|", "DISAB"); 471 | } else { 472 | System.out.printf("|%7s|", " "); 473 | } 474 | } 475 | System.out.println(); 476 | for (ConnectionGene con : connections) { 477 | System.out.printf("========="); 478 | } 479 | System.out.println(); 480 | } 481 | 482 | @Override 483 | public int compareTo(Genome genome) { 484 | if (this.fitness == genome.fitness) return 0; 485 | 486 | return this.fitness > genome.fitness ? 1 : -1; 487 | } 488 | 489 | private float sigmoid(float x) { 490 | return (float)(1 / (1 + Math.exp(-4.9 * x))); 491 | } 492 | 493 | @Override 494 | public void run() { 495 | evaluate(); 496 | } 497 | } 498 | -------------------------------------------------------------------------------- /src/com/jade/dataStructures/InnovationGenerator.java: -------------------------------------------------------------------------------- 1 | package com.jade.dataStructures; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class InnovationGenerator { 7 | private int currentInnovation = 0; 8 | 9 | public List innovationTracker; 10 | 11 | public InnovationGenerator() { 12 | innovationTracker = new ArrayList(); 13 | } 14 | 15 | public int getInnovation(ConnectionGene newCon) { 16 | for (ConnectionGene con : innovationTracker) { 17 | if (con.getInNode().getType() == newCon.getInNode().getType() && con.getInNode().getId() == newCon.getInNode().getId() && 18 | con.getOutNode().getType() == newCon.getOutNode().getType() && con.getOutNode().getId() == newCon.getOutNode().getId()) { 19 | return con.getInnovation(); 20 | } 21 | } 22 | innovationTracker.add(newCon); 23 | return currentInnovation++; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/com/jade/dataStructures/JColor.java: -------------------------------------------------------------------------------- 1 | package com.jade.dataStructures; 2 | 3 | import com.jade.file.Parser; 4 | import com.jade.file.Serialize; 5 | 6 | import java.awt.Color; 7 | 8 | public class JColor extends Serialize { 9 | public Color color; 10 | int r; 11 | int g; 12 | int b; 13 | 14 | public JColor(int r, int g, int b) { 15 | this.r = r; 16 | this.g = g; 17 | this.b = b; 18 | this.color = new Color((float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f); 19 | } 20 | 21 | @Override 22 | public String serialize(int tabSize) { 23 | StringBuilder builder = JString.getBuilder(); 24 | 25 | builder.append(beginObjectProperty("JColor", tabSize)); 26 | builder.append(addIntProperty("r", r, tabSize + 1, true, true)); 27 | builder.append(addIntProperty("g", g, tabSize + 1, true, true)); 28 | builder.append(addIntProperty("b", b, tabSize + 1, true, false)); 29 | builder.append(closeObjectProperty(tabSize)); 30 | 31 | return builder.toString(); 32 | } 33 | 34 | public static JColor deserialize() { 35 | Parser.consumeBeginObjectProperty(); 36 | 37 | int r = Parser.consumeIntProperty("r"); 38 | Parser.consume(','); 39 | int g = Parser.consumeIntProperty("g"); 40 | Parser.consume(','); 41 | int b = Parser.consumeIntProperty("b"); 42 | Parser.consume('}'); 43 | 44 | return new JColor(r, g, b); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/com/jade/dataStructures/JImage.java: -------------------------------------------------------------------------------- 1 | package com.jade.dataStructures; 2 | 3 | import com.jade.components.Component; 4 | 5 | import java.awt.image.BufferedImage; 6 | import java.awt.image.DataBufferByte; 7 | 8 | public class JImage extends Component { 9 | public BufferedImage image; 10 | 11 | public JImage(BufferedImage subImg) { 12 | this.image = subImg; 13 | } 14 | 15 | @Override 16 | public JImage clone() { 17 | return new JImage(this.image); 18 | } 19 | 20 | @Override 21 | public String serialize(int tabSize) { 22 | StringBuilder builder = JString.getBuilder(); 23 | 24 | builder.append(beginObjectProperty("JImage", tabSize)); 25 | builder.append(addIntProperty("width", image.getWidth(), tabSize + 1, true, true)); 26 | builder.append(addIntProperty("height", image.getHeight(), tabSize + 1, true, true)); 27 | 28 | byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); 29 | builder.append(addByteArrayProperty("imageData", pixels, tabSize + 1, true, false)); 30 | builder.append(closeObjectProperty(tabSize)); 31 | 32 | return builder.toString(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/com/jade/dataStructures/JMath.java: -------------------------------------------------------------------------------- 1 | package com.jade.dataStructures; 2 | 3 | public class JMath { 4 | public static boolean nearF(double x, double y,double precision) { 5 | return x >= y - precision && x <= y + precision; 6 | } 7 | 8 | public static boolean nearF(float x, float y, float precision) { 9 | return x >= y - precision && x <= y + precision; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/com/jade/dataStructures/JString.java: -------------------------------------------------------------------------------- 1 | package com.jade.dataStructures; 2 | 3 | public class JString { 4 | private static StringBuilder builder = null; 5 | 6 | public static StringBuilder getBuilder() { 7 | if (JString.builder == null) { 8 | JString.builder = new StringBuilder(); 9 | } 10 | 11 | JString.builder = new StringBuilder(); 12 | return JString.builder; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/com/jade/dataStructures/Matrix.java: -------------------------------------------------------------------------------- 1 | package com.jade.dataStructures; 2 | 3 | public class Matrix { 4 | public int numRows, numColumns; 5 | public float data[][]; 6 | 7 | public Matrix(int rows, int cols) { 8 | this.numRows = rows; 9 | this.numColumns = cols; 10 | data = new float[this.numRows][this.numColumns]; 11 | for (int i=0; i < this.numRows; i++) { 12 | for (int j=0; j < this.numColumns; j++) { 13 | this.data[i][j] = 0.0f; 14 | } 15 | } 16 | } 17 | 18 | public void randomize() { 19 | for (int i=0; i < this.numRows; i++) { 20 | for (int j=0; j < this.numColumns; j++) { 21 | this.data[i][j] = (float)(Math.random() * 2.0 - 1); 22 | } 23 | } 24 | } 25 | 26 | public Matrix copy() { 27 | Matrix output = new Matrix(this.numRows, this.numColumns); 28 | for (int i=0; i < output.data.length; i++) { 29 | for (int j=0; j < output.data[0].length; j++) { 30 | output.data[i][j] = this.data[i][j]; 31 | } 32 | } 33 | 34 | return output; 35 | } 36 | 37 | public static Matrix transpose(Matrix m) { 38 | Matrix res = new Matrix(m.numColumns, m.numRows); 39 | 40 | for (int i=0; i < m.numRows; i++) { 41 | for (int j=0; j < m.numColumns; j++) { 42 | res.data[j][i] = m.data[i][j]; 43 | } 44 | } 45 | 46 | return res; 47 | } 48 | 49 | public static Matrix multiply(Matrix a, Matrix b) throws Exception { 50 | if (a.numColumns != b.numRows) throw new Exception("Matrix.multiply(a, b) expects a.numColumns == b.numRows"); 51 | 52 | Matrix res = new Matrix(a.numRows, b.numColumns); 53 | for (int i=0; i < a.numRows; i++) { 54 | for (int j=0; j < b.numColumns; j++) { 55 | float tmp = 0; 56 | for (int k=0; k < a.numColumns; k++) { 57 | tmp += (a.data[i][k] * b.data[k][j]); 58 | } 59 | res.data[i][j] = tmp; 60 | } 61 | } 62 | 63 | return res; 64 | } 65 | 66 | public void multiply(int n) { 67 | for (int i=0; i < this.numRows; i++) { 68 | for (int j=0; j < this.numColumns; j++) { 69 | this.data[i][j] *= n; 70 | } 71 | } 72 | } 73 | 74 | public void multiply(float n) { 75 | for (int i=0; i < this.numRows; i++) { 76 | for (int j=0; j < this.numColumns; j++) { 77 | this.data[i][j] *= n; 78 | } 79 | } 80 | } 81 | 82 | public void add(Matrix n) throws Exception { 83 | if (n.numColumns != this.numColumns || n.numRows != this.numRows) throw new Exception("Matrix.add(n) must use to matrixes of the same depth and width"); 84 | 85 | for (int i=0; i < this.numRows; i++) { 86 | for (int j=0; j < this.numColumns; j++) { 87 | this.data[i][j] += n.data[i][j]; 88 | } 89 | } 90 | } 91 | 92 | public float[][] toArray() { 93 | float[][] res = new float[this.numColumns][this.numRows]; 94 | for (int i=0; i < this.numRows; i++) { 95 | for (int j=0; j < this.numColumns; j++) { 96 | res[i][j] = this.data[i][j]; 97 | } 98 | } 99 | 100 | return res; 101 | } 102 | 103 | public static Matrix toMatrix(float[][] input) { 104 | Matrix m = new Matrix(input.length, input[0].length); 105 | for (int i=0; i < input.length; i++) { 106 | for (int j=0; j < input[0].length; j++) { 107 | m.data[i][j] = input[i][j]; 108 | } 109 | } 110 | return m; 111 | } 112 | 113 | public void print() { 114 | System.out.println("Matrix"); 115 | for (int i=0; i < this.numRows; i++) { 116 | for (int j=0; j < this.numColumns; j++) { 117 | System.out.printf("%5.2f ", this.data[i][j]); 118 | } 119 | System.out.println(); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/com/jade/dataStructures/NodeGene.java: -------------------------------------------------------------------------------- 1 | package com.jade.dataStructures; 2 | 3 | import com.jade.components.Component; 4 | 5 | import java.awt.*; 6 | 7 | public class NodeGene extends Component implements Comparable { 8 | 9 | public enum Type { 10 | INPUT, 11 | HIDDEN, 12 | OUTPUT, 13 | ; 14 | } 15 | 16 | private Type type; 17 | private int id; 18 | private float value; 19 | private boolean debug = true; 20 | 21 | public int layer; 22 | public float x, y; 23 | 24 | public NodeGene(Type type, int id, float value, int layer) { 25 | this.type = type; 26 | this.id = id; 27 | this.layer = layer; 28 | this.value = value; 29 | } 30 | 31 | public NodeGene(NodeGene toBeCopied) { 32 | type = toBeCopied.getType(); 33 | id = toBeCopied.getId(); 34 | layer = toBeCopied.layer; 35 | } 36 | 37 | public void addValue(float valueToAdd) { 38 | this.value += valueToAdd; 39 | } 40 | 41 | public float getValue() { 42 | return this.value; 43 | } 44 | 45 | public void setValue(float val) { 46 | this.value = val; 47 | } 48 | 49 | public Type getType() { 50 | return this.type; 51 | } 52 | 53 | public int getId() { 54 | return this.id; 55 | } 56 | 57 | public NodeGene copy() { 58 | return new NodeGene(this.type, this.id, this.value, this.layer); 59 | } 60 | 61 | @Override 62 | public Component clone() { 63 | return copy(); 64 | } 65 | 66 | @Override 67 | public void draw(Graphics2D g2) { 68 | if (debug) { 69 | g2.setColor(Color.BLACK); 70 | g2.drawOval((int) x, (int) y, 40, 40); 71 | g2.setColor(Color.WHITE); 72 | g2.fillOval((int) x, (int) y, 40, 40); 73 | 74 | g2.setColor(Color.BLACK); 75 | g2.setFont(new Font("Times New Roman", Font.PLAIN, 12)); 76 | g2.drawString("" + (id + 1), x + 17, y + 19); 77 | g2.drawString("" + value, x + 13, y + 32); 78 | } else { 79 | g2.setColor(Color.BLACK); 80 | g2.drawOval((int)x, (int)y, 10, 10); 81 | g2.setColor(Color.WHITE); 82 | g2.fillOval((int)x + 2, (int)y + 2, 8, 8); 83 | } 84 | } 85 | 86 | @Override 87 | public int compareTo(NodeGene gene) { 88 | if (gene.layer == this.layer) { 89 | if (id == gene.getId()) return 0; 90 | 91 | return gene.getId() > id ? -1 : 1; 92 | } 93 | 94 | return gene.layer > this.layer ? -1 : 1; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/com/jade/dataStructures/Pool.java: -------------------------------------------------------------------------------- 1 | package com.jade.dataStructures; 2 | 3 | 4 | public class Pool { 5 | public int generation; 6 | public int currentSpecies; 7 | public int currentGenome; 8 | public int currentFrame; 9 | public float maxFitness; 10 | public InnovationGenerator innovationGenerator; 11 | 12 | public Pool() { 13 | this.generation = 0; 14 | this.currentSpecies = 0; 15 | this.currentGenome = 0; 16 | this.currentFrame = 0; 17 | this.maxFitness = 0.0f; 18 | this.innovationGenerator = new InnovationGenerator(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/com/jade/dataStructures/SpecieManager.java: -------------------------------------------------------------------------------- 1 | package com.jade.dataStructures; 2 | 3 | import com.jade.main.Constants; 4 | 5 | import java.awt.*; 6 | import java.util.ArrayList; 7 | import java.util.Collections; 8 | import java.util.List; 9 | import java.util.Random; 10 | 11 | public class SpecieManager { 12 | InnovationGenerator innovationGenerator = new InnovationGenerator(); 13 | public final float C1 = 1.0f; 14 | public final float C2 = 1.0f; 15 | public final float C3 = 0.4f; 16 | public final float DELTA_T = 3.0f; 17 | public final float CROSSOVER_CHANCE = 0.75f; 18 | public final int MAX_STALENESS = 15; 19 | public final int POPULATION_SIZE = 150; 20 | 21 | public Pool pool; 22 | private Random random = new Random(); 23 | 24 | public List species = new ArrayList<>(); 25 | 26 | public SpecieManager(int inputSize, int outputSize) { 27 | List inputs = new ArrayList<>(); 28 | List outputs = new ArrayList<>(); 29 | int globalCounter = 0; 30 | for (int i=0; i < inputSize; i++) { 31 | inputs.add(new NodeGene(NodeGene.Type.INPUT, globalCounter, 0.0f, 0)); 32 | globalCounter++; 33 | } 34 | 35 | for (int i=0; i < outputSize; i++) { 36 | outputs.add(new NodeGene(NodeGene.Type.OUTPUT, globalCounter, 0.0f, 1)); 37 | globalCounter++; 38 | } 39 | 40 | initialize(inputs, outputs); 41 | } 42 | 43 | public Genome breedChild(Species species) { 44 | Genome child; 45 | if (random.nextFloat() < CROSSOVER_CHANCE) { 46 | Genome g1 = species.population.get(random.nextInt(species.population.size())); 47 | Genome g2 = species.population.get(random.nextInt(species.population.size())); 48 | child = Genome.makeBaby(g1, g2); 49 | } else { 50 | Genome g = species.population.get(random.nextInt(species.population.size())); 51 | child = g.copy(); 52 | } 53 | 54 | child.mutate(innovationGenerator); 55 | 56 | return child; 57 | } 58 | 59 | public void newGeneration() { 60 | cullSpecies(false); 61 | //System.out.println("1: Size: " + species.size()); 62 | rankGlobally(); 63 | removeStaleSpecies(); 64 | //System.out.println("2: Size: " + species.size()); 65 | rankGlobally(); 66 | removeWeakSpecies(); 67 | //System.out.println("3: Size: " + species.size()); 68 | //System.out.println(); 69 | 70 | float sum = totalAverageFitness(); 71 | List children = new ArrayList<>(); 72 | for (Species s : species) { 73 | double breed = Math.floor(s.getAverageFitness() / sum * s.population.size()) - 1; 74 | for (int i=0; i < breed; i++) { 75 | children.add(breedChild(s)); 76 | } 77 | } 78 | cullSpecies(true); 79 | if (species.size() == 0) { 80 | System.out.println("No increase in fitness for all species."); 81 | System.exit(-1); 82 | } 83 | 84 | while (children.size() + species.size() < POPULATION_SIZE) { 85 | Species s = species.get(random.nextInt(species.size())); 86 | children.add(breedChild(s)); 87 | } 88 | 89 | for (Genome child : children) { 90 | addToSpecies(child); 91 | } 92 | 93 | pool.generation++; 94 | } 95 | 96 | public void initialize(List inputs, List outputs) { 97 | pool = new Pool(); 98 | 99 | for(int i=0; i < POPULATION_SIZE; i++) { 100 | Genome basic = Genome.basicGenome(inputs, outputs, pool.innovationGenerator); 101 | addToSpecies(basic); 102 | } 103 | } 104 | 105 | public Genome nextGenome() { 106 | if (species.get(pool.currentSpecies).population.get(pool.currentGenome).getFitness() > pool.maxFitness) { 107 | pool.maxFitness = species.get(pool.currentSpecies).population.get(pool.currentGenome).getFitness(); 108 | } 109 | 110 | if (pool.currentGenome + 1 < species.get(pool.currentSpecies).population.size()) { 111 | pool.currentGenome++; 112 | species.get(pool.currentSpecies).population.get(pool.currentGenome).run(); 113 | return species.get(pool.currentSpecies).population.get(pool.currentGenome); 114 | } 115 | 116 | pool.currentSpecies++; 117 | if (pool.currentSpecies < species.size()) { 118 | pool.currentGenome = 0; 119 | species.get(pool.currentSpecies).population.get(pool.currentGenome).run(); 120 | return species.get(pool.currentSpecies).population.get(pool.currentGenome); 121 | } 122 | 123 | newGeneration(); 124 | pool.currentSpecies = 0; 125 | pool.currentGenome = 0; 126 | return nextGenome(); 127 | } 128 | 129 | public void removeStaleSpecies() { 130 | List survived = new ArrayList<>(); 131 | Collections.sort(species); 132 | 133 | int i = 1; 134 | for (Species currentSpecie : species) { 135 | currentSpecie.sortByFitnessDesc(); 136 | 137 | if (currentSpecie.population.get(0).getFitness() > currentSpecie.topFitness) { 138 | currentSpecie.topFitness = currentSpecie.population.get(0).getFitness(); 139 | currentSpecie.staleness = 0; 140 | } else { 141 | currentSpecie.staleness++; 142 | } 143 | 144 | if (currentSpecie.staleness < MAX_STALENESS) { 145 | survived.add(currentSpecie); 146 | } else if (i < 3) { 147 | survived.add(currentSpecie); 148 | } 149 | i++; 150 | } 151 | 152 | species = survived; 153 | } 154 | 155 | public void removeWeakSpecies() { 156 | List survived = new ArrayList<>(); 157 | 158 | float sum = totalAverageFitness(); 159 | for (Species s : species) { 160 | double breed = Math.floor(s.getAverageFitness() / sum * s.population.size()); 161 | if (breed >= 1.0) { 162 | survived.add(s); 163 | } 164 | } 165 | 166 | species = survived; 167 | } 168 | 169 | public void addToSpecies(Genome child) { 170 | for (Species s : species) { 171 | if (sameSpecies(s.population.get(0), child)) { 172 | s.population.add(child); 173 | return; 174 | } 175 | } 176 | 177 | Species childSpecies = new Species(child); 178 | species.add(childSpecies); 179 | } 180 | 181 | public void cullSpecies(boolean cutToOne) { 182 | for (Species s : species) { 183 | s.sortByFitnessAsc(); 184 | 185 | double remaining = Math.ceil(s.population.size() / 2.0); 186 | if (cutToOne) remaining = 1.0; 187 | while (s.population.size() > remaining) { 188 | s.population.remove(0); 189 | } 190 | } 191 | } 192 | 193 | private float totalAverageFitness() { 194 | float sum = 0.0f; 195 | for (Species s : species) { 196 | sum += s.getAverageFitness(); 197 | } 198 | return sum; 199 | } 200 | 201 | private boolean sameSpecies(Genome g1, Genome g2) { 202 | float similarity = Genome.compatibilityDistance(g1, g2, C1, C2, C3); 203 | return similarity < DELTA_T; 204 | } 205 | 206 | private void rankGlobally() { 207 | List global = new ArrayList<>(); 208 | for (Species s : species) { 209 | global.addAll(s.population); 210 | } 211 | Collections.sort(global); 212 | 213 | for (int i=0; i < global.size(); i++) { 214 | global.get(i).globalRank = i; 215 | } 216 | } 217 | 218 | public void draw(Graphics2D g2, int fitness, float evaluation) { 219 | g2.setColor(new Color(0.0f, 0.0f, 0.0f, 0.7f)); 220 | g2.fillRect(760, 0, Constants.SCREEN_WIDTH, 220); 221 | 222 | g2.setColor(Color.WHITE); 223 | g2.setFont(new Font("Times New Roman", Font.PLAIN, 16)); 224 | g2.drawString("Generation: " + pool.generation, 800, 165); 225 | g2.drawString("Species: " + pool.currentSpecies, 800, 185); 226 | g2.drawString("Genome: " + pool.currentGenome, 800, 205); 227 | g2.drawString("Fitness: " + fitness, 900, 165); 228 | g2.drawString("Max Fitness: " + (int)(pool.maxFitness * 100), 900, 185); 229 | g2.drawString("Eval: " + evaluation, 900, 205); 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /src/com/jade/dataStructures/Species.java: -------------------------------------------------------------------------------- 1 | package com.jade.dataStructures; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | public class Species implements Comparable { 8 | public int staleness; 9 | 10 | public List population; 11 | public Genome representative; 12 | public float topFitness = 0.0f; 13 | 14 | public Species(Genome representative) { 15 | population = new ArrayList<>(); 16 | staleness = 0; 17 | population.add(representative); 18 | this.representative = representative; 19 | } 20 | 21 | public void sortByFitnessDesc() { 22 | Collections.sort(population, Collections.reverseOrder()); 23 | } 24 | 25 | public void sortByFitnessAsc() { 26 | Collections.sort(population); 27 | } 28 | 29 | public float getAverageFitness() { 30 | float sum = 0.0f; 31 | for (Genome g : population) { 32 | sum += g.getFitness(); 33 | } 34 | 35 | return sum / population.size(); 36 | } 37 | 38 | @Override 39 | public int compareTo(Species species) { 40 | if (this.getAverageFitness() == species.getAverageFitness()) return 0; 41 | 42 | return this.getAverageFitness() > species.getAverageFitness() ? 1 : -1; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/com/jade/dataStructures/Vector2.java: -------------------------------------------------------------------------------- 1 | package com.jade.dataStructures; 2 | 3 | import com.jade.file.Parser; 4 | import com.jade.file.Serialize; 5 | 6 | public class Vector2 extends Serialize { 7 | public float x; 8 | public float y; 9 | 10 | public Vector2() { 11 | this.x = 0.0f; 12 | this.y = 0.0f; 13 | } 14 | 15 | public Vector2(float x, float y) { 16 | this.x = x; 17 | this.y = y; 18 | } 19 | 20 | public float length() { 21 | return (float)Math.sqrt((x * x) + (y * y)); 22 | } 23 | 24 | public float lengthSquared() { 25 | return (x * x) + (y * y); 26 | } 27 | 28 | public static Vector2 normalize(Vector2 value) { 29 | float v = 1.0f / value.length(); 30 | return new Vector2(value.x * v, value.y * v); 31 | } 32 | 33 | public static Vector2 add(Vector2 value1, Vector2 value2) { 34 | return new Vector2(value1.x + value2.x, value1.y + value2.y); 35 | } 36 | 37 | public static Vector2 subtract(Vector2 value1, Vector2 value2) { 38 | return new Vector2(value1.x - value2.x, value1.y - value2.y); 39 | } 40 | 41 | public static Vector2 scale(Vector2 value, float scale) { 42 | return new Vector2(value.x * scale, value.y * scale); 43 | } 44 | 45 | public static Vector2 scale(Vector2 value, double scale) { 46 | return new Vector2(value.x * (float)scale, value.y * (float)scale); 47 | } 48 | 49 | public static double dot(Vector2 v1, Vector2 v2) { 50 | return v1.x * v2.x + v1.y * v2.y; 51 | } 52 | 53 | public static double component(Vector2 vector, Vector2 directionVector) { 54 | double alpha = Math.atan2(directionVector.y, directionVector.x); 55 | double theta = Math.atan2(vector.y, vector.x); 56 | double mag = vector.length(); 57 | double a = mag * Math.cos(theta - alpha); 58 | return a; 59 | } 60 | 61 | public static Vector2 componentVector(Vector2 vector, Vector2 directionVector) { 62 | Vector2 v = Vector2.normalize(directionVector); 63 | return Vector2.scale(v, Vector2.component(vector, directionVector)); 64 | } 65 | 66 | @Override 67 | public String toString() { 68 | return "Vec2<" + this.x + ", " + this.y + ">"; 69 | } 70 | 71 | @Override 72 | public String serialize(int tabSize) { 73 | StringBuilder builder = JString.getBuilder(); 74 | 75 | builder.append(beginObjectProperty("Vector2", tabSize)); 76 | builder.append(addFloatProperty("x", x, tabSize + 1, true, true)); 77 | builder.append(addFloatProperty("y", y, tabSize + 1, true, false)); 78 | builder.append(closeObjectProperty(tabSize)); 79 | 80 | return builder.toString(); 81 | } 82 | 83 | public static Vector2 deserialize() { 84 | String title = Parser.parseString(); 85 | Parser.checkString("Vector2", title); 86 | Parser.consume(':'); 87 | Parser.consume('{'); 88 | 89 | String xTitle = Parser.parseString(); 90 | Parser.checkString("x", xTitle); 91 | Parser.consume(':'); 92 | float x = Parser.parseFloat(); 93 | Parser.consume(','); 94 | 95 | String yTitle = Parser.parseString(); 96 | Parser.checkString("y", yTitle); 97 | Parser.consume(':'); 98 | float y = Parser.parseFloat(); 99 | Parser.consume('}'); 100 | 101 | return new Vector2(x, y); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/com/jade/file/Parser.java: -------------------------------------------------------------------------------- 1 | package com.jade.file; 2 | 3 | import com.jade.components.TriangleBounds; 4 | import com.jade.dataStructures.JString; 5 | import com.jade.components.BoxBounds; 6 | import com.jade.components.Component; 7 | import com.jade.jade.GameObject; 8 | import com.jade.components.SubSprite; 9 | import com.jade.scripts.Portal; 10 | 11 | public class Parser { 12 | 13 | public static int offset = 0; 14 | public static int line = 1; 15 | public static byte[] bytes; 16 | 17 | public static void init(int offset, byte[] bytes) { 18 | Parser.offset = offset; 19 | Parser.bytes = bytes; 20 | } 21 | 22 | public static GameObject getNextGameObject() { 23 | if (bytes.length == 0 || offset >= bytes.length) return null; 24 | 25 | if (peek() == ',') Parser.consume(','); 26 | skipWhiteSpace(); 27 | if (offset >= bytes.length - 1) return null; 28 | 29 | GameObject go = GameObject.deserialize(); 30 | return go; 31 | } 32 | 33 | public static void skipWhiteSpace() { 34 | char c = '\0'; 35 | while (offset != bytes.length - 1 && (peek() == ' ' || peek() == '\t' || peek() == '\n' || peek() == '\r' || peek() == '\f')) { 36 | if (peek() == '\n') Parser.line++; 37 | advance(); 38 | } 39 | } 40 | 41 | public static String parseString() { 42 | skipWhiteSpace(); 43 | char c; 44 | StringBuilder builder = JString.getBuilder(); 45 | 46 | consume('"'); 47 | 48 | while (offset != bytes.length - 1 && peek() != '"') { 49 | c = advance(); 50 | builder.append(c); 51 | } 52 | consume('"'); 53 | 54 | return builder.toString(); 55 | } 56 | 57 | public static boolean parseBoolean() { 58 | skipWhiteSpace(); 59 | char c; 60 | StringBuilder builder = JString.getBuilder(); 61 | 62 | if (offset != bytes.length - 1 && peek() == 't') { 63 | c = advance(); 64 | builder.append(c); 65 | consume('r'); 66 | builder.append('r'); 67 | consume('u'); 68 | builder.append('u'); 69 | consume('e'); 70 | builder.append('e'); 71 | } else if (offset != bytes.length - 1 && peek() == 'f') { 72 | c = advance(); 73 | builder.append(c); 74 | consume('a'); 75 | builder.append('a'); 76 | consume('l'); 77 | builder.append('l'); 78 | consume('s'); 79 | builder.append('s'); 80 | consume('e'); 81 | builder.append('e'); 82 | } else { 83 | System.out.println("Error: Expected 'true' or 'false'. Instead got '" + peek() + "' at line: " + line); 84 | System.exit(-1); 85 | } 86 | 87 | return builder.toString().compareTo("true") == 0; 88 | } 89 | 90 | public static float parseFloat() { 91 | skipWhiteSpace(); 92 | char c; 93 | StringBuilder builder = JString.getBuilder(); 94 | 95 | while (isDigit(peek()) || peek() == '.' || peek() == 'f' || peek() == '-') { 96 | c = advance(); 97 | builder.append(c); 98 | } 99 | 100 | return Float.parseFloat(builder.toString()); 101 | } 102 | 103 | public static double parseDouble() { 104 | skipWhiteSpace(); 105 | char c; 106 | StringBuilder builder = JString.getBuilder(); 107 | 108 | while (isDigit(peek()) || peek() == '.' || peek() == '-') { 109 | c = advance(); 110 | builder.append(c); 111 | } 112 | 113 | return Float.parseFloat(builder.toString()); 114 | } 115 | 116 | public static int parseInt() { 117 | skipWhiteSpace(); 118 | char c; 119 | StringBuilder builder = JString.getBuilder(); 120 | 121 | while (isDigit(peek()) || peek() == '-') { 122 | c = advance(); 123 | builder.append(c); 124 | } 125 | 126 | return Integer.parseInt(builder.toString()); 127 | } 128 | 129 | public static char peek() { 130 | if (offset >= bytes.length) return '\0'; 131 | return (char)bytes[offset]; 132 | } 133 | 134 | public static boolean isDigit(char c) { 135 | return c >= '0' && c <= '9'; 136 | } 137 | 138 | public static void consume(char c) { 139 | skipWhiteSpace(); 140 | char actual = (char)bytes[offset]; 141 | if (actual != c) { 142 | System.out.println("Error: Expected '" + c + "' instead got '" + actual + "' at line: " + Parser.line); 143 | System.exit(-1); 144 | } 145 | offset++; 146 | } 147 | 148 | public static char advance() { 149 | char c = (char)bytes[offset]; 150 | offset++; 151 | return c; 152 | } 153 | 154 | public static void checkString(String s1, String s2) { 155 | if (s1.compareTo(s2) != 0) { 156 | System.out.println("Error: Expected \"" + s1 + "\" instead got \"" + s2 + "\" at line: " + Parser.line); 157 | System.exit(-1); 158 | } 159 | } 160 | 161 | public static Component parseComponent() { 162 | String componentTitle = Parser.parseString(); 163 | switch (componentTitle) { 164 | case "SubSprite": 165 | return SubSprite.deserialize(); 166 | case "BoxBounds": 167 | return BoxBounds.deserialize(); 168 | case "TriangleBounds": 169 | return TriangleBounds.deserialize(); 170 | case "Portal": 171 | return Portal.deserialize(); 172 | default: 173 | System.out.println("Could not find a component of type \"" + componentTitle + "\" at line: " + Parser.line); 174 | System.exit(-1); 175 | } 176 | 177 | return null; 178 | } 179 | 180 | 181 | // Helper functions 182 | public static void consumeBeginObjectProperty() { 183 | consume(':'); 184 | consume('{'); 185 | } 186 | 187 | public static void consumeBeginObjectProperty(String name) { 188 | String title = parseString(); 189 | checkString(name, title); 190 | consumeBeginObjectProperty(); 191 | } 192 | 193 | public static String consumeStringProperty(String propertyName) { 194 | String title = parseString(); 195 | checkString(propertyName, title); 196 | consume(':'); 197 | return parseString(); 198 | } 199 | 200 | public static int consumeIntProperty(String propertyName) { 201 | String title = parseString(); 202 | checkString(propertyName, title); 203 | consume(':'); 204 | return parseInt(); 205 | } 206 | 207 | public static float consumeFloatProperty(String propertyName) { 208 | String title = parseString(); 209 | checkString(propertyName, title); 210 | consume(':'); 211 | return parseFloat(); 212 | } 213 | 214 | public static double consumeDoubleProperty(String propertyName) { 215 | String title = parseString(); 216 | checkString(propertyName, title); 217 | consume(':'); 218 | return parseDouble(); 219 | } 220 | 221 | public static boolean consumeBooleanProperty(String propertyName) { 222 | String title = parseString(); 223 | checkString(propertyName, title); 224 | consume(':'); 225 | return parseBoolean(); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/com/jade/file/Serialize.java: -------------------------------------------------------------------------------- 1 | package com.jade.file; 2 | 3 | import com.jade.dataStructures.JString; 4 | 5 | public abstract class Serialize { 6 | public abstract String serialize(int tabSize); 7 | 8 | public String addTabs(int tabSize) { 9 | String str = ""; 10 | for (int i=0; i < tabSize; i++) { 11 | str += "\t"; 12 | } 13 | return str; 14 | } 15 | 16 | public String addString(String str, int tabSize) { 17 | return addTabs(tabSize) + str; 18 | } 19 | 20 | public String addStringProperty(String name, String value, int tabSize, boolean newline, boolean comma) { 21 | return addTabs(tabSize) + "\"" + name + "\": " + "\"" + value + "\"" + addEnding(newline, comma); 22 | } 23 | 24 | public String addIntProperty(String name, int value, int tabSize, boolean newline, boolean comma) { 25 | return addTabs(tabSize) + "\"" + name + "\": " + value + addEnding(newline, comma); 26 | } 27 | 28 | public String addFloatProperty(String name, float value, int tabSize, boolean newline, boolean comma) { 29 | return addTabs(tabSize) + "\"" + name + "\": " + value + "f" + addEnding(newline, comma); 30 | } 31 | 32 | public String addDoubleProperty(String name, double value, int tabSize, boolean newline, boolean comma) { 33 | return addTabs(tabSize) + "\"" + name + "\": " + value + addEnding(newline, comma); 34 | } 35 | 36 | public String addBooleanProperty(String name, boolean value, int tabSize, boolean newline, boolean comma) { 37 | return addTabs(tabSize) + "\"" + name + "\": " + value + addEnding(newline, comma); 38 | } 39 | 40 | public String addIntArrayProperty(String name, int[] data, int tabSize, boolean newline, boolean comma) { 41 | StringBuilder builder = JString.getBuilder(); 42 | builder.append(addTabs(tabSize) + "\"" + name + "\": ["); 43 | for (int i=0; i < data.length; i++) { 44 | if (i != data.length - 1) 45 | builder.append("" + data[i] + ", "); 46 | else 47 | builder.append("" + data[i]); 48 | } 49 | builder.append("]"); 50 | builder.append(addEnding(newline, comma)); 51 | 52 | return builder.toString(); 53 | } 54 | 55 | public String addByteArrayProperty(String name, byte[] data, int tabSize, boolean newline, boolean comma) { 56 | StringBuilder builder = JString.getBuilder(); 57 | builder.append(addTabs(tabSize) + "\"" + name + "\": ["); 58 | for (int i=0; i < data.length; i++) { 59 | if (i != data.length - 1) 60 | builder.append("" + data[i] + ", "); 61 | else 62 | builder.append("" + data[i]); 63 | } 64 | builder.append("]"); 65 | builder.append(addEnding(newline, comma)); 66 | 67 | return builder.toString(); 68 | } 69 | 70 | public String beginObjectProperty(String name, int tabSize) { 71 | return addTabs(tabSize) + "\"" + name + "\": {" + addEnding(true, false); 72 | } 73 | 74 | public String closeObjectProperty(int tabSize) { 75 | return addTabs(tabSize) + "}"; 76 | } 77 | 78 | public String addEnding(boolean newline, boolean comma) { 79 | String str = ""; 80 | if (comma) str += ","; 81 | if (newline) str += "\n"; 82 | return str; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/com/jade/jade/AssetPool.java: -------------------------------------------------------------------------------- 1 | package com.jade.jade; 2 | 3 | import com.jade.components.Sprite; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | public class AssetPool { 9 | static Map sprites = new HashMap<>(); 10 | 11 | public static boolean hasSprite(String pictureFile) { 12 | return AssetPool.sprites.containsKey(pictureFile); 13 | } 14 | 15 | public static Sprite getSprite(String pictureFile) { 16 | if (AssetPool.hasSprite(pictureFile)) { 17 | return AssetPool.sprites.get(pictureFile); 18 | } 19 | 20 | return null; 21 | } 22 | 23 | public static void addSprite(String pictureFile, Sprite sprite) { 24 | if (!AssetPool.hasSprite(pictureFile)) { 25 | AssetPool.sprites.put(pictureFile, sprite); 26 | } 27 | } 28 | 29 | public static void clearSprites() { 30 | AssetPool.sprites.clear(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/com/jade/jade/Camera.java: -------------------------------------------------------------------------------- 1 | package com.jade.jade; 2 | 3 | public class Camera { 4 | public Transform transform; 5 | 6 | public Camera(Transform transform) { 7 | this.transform = transform; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/com/jade/jade/GameObject.java: -------------------------------------------------------------------------------- 1 | package com.jade.jade; 2 | 3 | import java.awt.Graphics2D; 4 | import java.awt.Color; 5 | import java.awt.Font; 6 | 7 | import com.jade.components.Component; 8 | import com.jade.dataStructures.JString; 9 | import com.jade.file.Parser; 10 | import com.jade.file.Serialize; 11 | 12 | import java.util.List; 13 | import java.util.ArrayList; 14 | 15 | public class GameObject extends Serialize implements Comparable { 16 | List components; 17 | List componentsToRemove = new ArrayList<>(); 18 | List componentsToAdd = new ArrayList<>(); 19 | 20 | private boolean debugMode = false; 21 | public String name; 22 | public Transform transform; 23 | public List gameObjects; 24 | public GameObject parent; 25 | public boolean isUi = false; 26 | public int zIndex = 0; 27 | 28 | public GameObject(String name, Transform transform) { 29 | components = new ArrayList(); 30 | gameObjects = new ArrayList(); 31 | this.name = name; 32 | this.transform = transform; 33 | this.parent = null; 34 | } 35 | 36 | public GameObject(String name) { 37 | // Only meant to be called for a "Scene" game object 38 | // a game object that encapsulates everything 39 | this.name = name; 40 | this.transform = null; 41 | } 42 | 43 | public void setZIndex(int newIndex) { 44 | this.zIndex = newIndex; 45 | } 46 | 47 | public void setUi(boolean set) { 48 | isUi = set; 49 | } 50 | 51 | public T getComponent(Class componentClass) { 52 | for (Component c : components) { 53 | if (componentClass.isAssignableFrom(c.getClass())) { 54 | try { 55 | return componentClass.cast(c); 56 | } catch (ClassCastException e) { 57 | e.printStackTrace(); 58 | System.exit(-1); 59 | } 60 | } 61 | } 62 | 63 | return null; 64 | } 65 | 66 | private void removeComponent(Component comp) { 67 | for (Component c : components) { 68 | if (c == comp) { 69 | components.remove(c); 70 | return; 71 | } 72 | } 73 | } 74 | 75 | public void safeRemoveComponent(Class componentClass) { 76 | for (Component c : components) { 77 | if (componentClass.isAssignableFrom(c.getClass())) { 78 | componentsToRemove.add(c); 79 | return; 80 | } 81 | } 82 | } 83 | 84 | public void addComponent(Component c) { 85 | c.parent = this; 86 | components.add(c); 87 | c.start(); 88 | } 89 | 90 | public void safeAddComponent(Component c) { 91 | componentsToAdd.add(c); 92 | } 93 | 94 | public void draw(Graphics2D g2) { 95 | if (debugMode) { 96 | g2.setColor(Color.BLUE); 97 | g2.setFont(new Font("Times New Roman", Font.PLAIN, 20)); 98 | g2.drawString(name, transform.position.x, transform.position.y); 99 | } 100 | 101 | for (Component c : components) { 102 | c.draw(g2); 103 | } 104 | } 105 | 106 | public void update(double dt) { 107 | for (Component c : components) { 108 | c.update(dt); 109 | } 110 | 111 | if (componentsToAdd.size() > 0) { 112 | for (Component c : componentsToAdd) { 113 | addComponent(c); 114 | } 115 | componentsToAdd.clear(); 116 | } 117 | 118 | if (componentsToRemove.size() > 0) { 119 | for (Component c : componentsToRemove) { 120 | removeComponent(c); 121 | } 122 | componentsToRemove.clear(); 123 | } 124 | } 125 | 126 | public List getAllComponents() { 127 | return this.components; 128 | } 129 | 130 | public void addGameObject(GameObject newObj) { 131 | this.gameObjects.add(newObj); 132 | newObj.parent = this; 133 | } 134 | 135 | public GameObject clone() { 136 | GameObject go = new GameObject(this.name, this.transform.clone()); 137 | for (Component c : components) { 138 | go.addComponent(c.clone()); 139 | } 140 | for (GameObject g : gameObjects) { 141 | go.gameObjects.add(g.clone()); 142 | } 143 | return go; 144 | } 145 | 146 | @Override 147 | public int compareTo(GameObject go) { 148 | if (go.transform.position.y != this.transform.position.y) 149 | return this.transform.position.y > go.transform.position.y ? 1 : -1; 150 | 151 | if (go.transform.position.x == this.transform.position.x) return 0; 152 | return this.transform.position.x > go.transform.position.x ? 1 : -1; 153 | } 154 | 155 | @Override 156 | public String toString() { 157 | return this.name; 158 | } 159 | 160 | @Override 161 | public String serialize(int tabSize) { 162 | if (name == "Nonserialize") return ""; 163 | 164 | StringBuilder builder = JString.getBuilder(); 165 | 166 | // GameObject 167 | builder.append(beginObjectProperty("GameObject", tabSize)); 168 | 169 | // Transform 170 | builder.append(transform.serialize(tabSize + 1) + addEnding(true, true)); 171 | 172 | // Z Index 173 | builder.append(addIntProperty("zIndex", zIndex, tabSize + 1, true, true)); 174 | 175 | // Name 176 | if (components.size() > 0 || gameObjects.size() > 0) 177 | builder.append(addStringProperty("Name", name, tabSize + 1, true, true)); 178 | else 179 | builder.append(addStringProperty("Name", name, tabSize + 1, true, false)); 180 | 181 | // Components 182 | int i = 0; 183 | if (components.size() > 0) 184 | builder.append(beginObjectProperty("Components", tabSize + 1)); 185 | for (Component c : components) { 186 | String str = c.serialize(tabSize + 2); 187 | if (str.compareTo("") != 0) builder.append(str); 188 | if (str.compareTo("") != 0 && i != components.size() - 1) 189 | builder.append(addEnding(true, true)); 190 | else if (str.compareTo("") != 0) 191 | builder.append(addEnding(true, false)); 192 | i++; 193 | } 194 | 195 | // Add comma after components if needed 196 | if (components.size() > 0) 197 | builder.append(closeObjectProperty(tabSize + 1)); 198 | if (gameObjects.size() > 0) 199 | builder.append(addEnding(true, true)); 200 | else 201 | builder.append(addEnding(true, false)); 202 | 203 | // GameObjects 204 | i = 0; 205 | if (gameObjects.size() > 0) 206 | beginObjectProperty("GameObjects", tabSize + 1); 207 | for (GameObject go : gameObjects) { 208 | builder.append(go.serialize(tabSize + 2)); 209 | 210 | if (i != gameObjects.size() - 1) 211 | builder.append(addEnding(true, true)); 212 | else 213 | builder.append(addEnding(true, false)); 214 | i++; 215 | } 216 | if (gameObjects.size() > 0) 217 | closeObjectProperty(tabSize + 1); 218 | 219 | builder.append(closeObjectProperty(tabSize)); 220 | 221 | return builder.toString(); 222 | } 223 | 224 | public static GameObject deserialize() { 225 | String gameObjectTitle = Parser.parseString(); 226 | Parser.checkString("GameObject", gameObjectTitle); 227 | 228 | Parser.consume(':'); 229 | Parser.consume('{'); 230 | 231 | Transform transform = Transform.deserialize(); 232 | Parser.consume(','); 233 | 234 | int zIndex = Parser.consumeIntProperty("zIndex"); 235 | Parser.consume(','); 236 | 237 | String nameTitle = Parser.parseString(); 238 | Parser.checkString("Name", nameTitle); 239 | Parser.consume(':'); 240 | String name = Parser.parseString(); 241 | 242 | GameObject go = new GameObject(name, transform); 243 | 244 | String title = ""; 245 | if (Parser.peek() == ',') { 246 | Parser.consume(','); 247 | title = Parser.parseString(); 248 | if (title.compareTo("Components") == 0) { 249 | Parser.consume(':'); 250 | Parser.consume('{'); 251 | Component c = Parser.parseComponent(); 252 | go.addComponent(c); 253 | 254 | while (Parser.peek() == ',') { 255 | Parser.consume(','); 256 | c = Parser.parseComponent(); 257 | go.addComponent(c); 258 | } 259 | Parser.consume('}'); 260 | } 261 | } 262 | 263 | if (title.compareTo("GameObjects") == 0) { 264 | Parser.consume(':'); 265 | Parser.consume('{'); 266 | GameObject g = Parser.getNextGameObject(); 267 | go.addGameObject(g); 268 | 269 | while (Parser.peek() == ',') { 270 | Parser.consume(','); 271 | g = Parser.getNextGameObject(); 272 | go.addGameObject(g); 273 | } 274 | Parser.consume('}'); 275 | } 276 | 277 | Parser.consume('}'); 278 | go.setZIndex(zIndex); 279 | return go; 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /src/com/jade/jade/Renderer.java: -------------------------------------------------------------------------------- 1 | package com.jade.jade; 2 | 3 | import com.jade.dataStructures.Vector2; 4 | import com.jade.main.Constants; 5 | import com.jade.main.Scene; 6 | 7 | import java.awt.*; 8 | import java.util.ArrayList; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | public class Renderer { 14 | public Map> gameObjects; 15 | public Camera camera; 16 | 17 | public Renderer(Camera camera) { 18 | this.camera = camera; 19 | gameObjects = new HashMap<>(); 20 | } 21 | 22 | public void submit(GameObject go) { 23 | gameObjects.computeIfAbsent(go.zIndex, k -> new ArrayList()); 24 | gameObjects.get(go.zIndex).add(go); 25 | } 26 | 27 | public void remove(GameObject go) { 28 | for (List goList : gameObjects.values()) { 29 | if (goList.contains(go)) { 30 | goList.remove(go); 31 | break; 32 | } 33 | } 34 | } 35 | 36 | public void draw(Graphics2D g2) { 37 | int lowestZIndex = Integer.MAX_VALUE; 38 | int highestZIndex = Integer.MIN_VALUE; 39 | for (Integer i : gameObjects.keySet()) { 40 | if (i < lowestZIndex) lowestZIndex = i; 41 | if (i > highestZIndex) highestZIndex = i; 42 | } 43 | 44 | int zIndex = lowestZIndex; 45 | while (zIndex <= highestZIndex) { 46 | if (gameObjects.get(zIndex) == null) { 47 | zIndex++; 48 | continue; 49 | } 50 | for (GameObject gameObject : gameObjects.get(zIndex)) { 51 | if (!gameObject.isUi) { 52 | if (gameObject.transform.position.x > Scene.camera.transform.position.x - Constants.TILE_WIDTH * 2 && 53 | gameObject.transform.position.x < Scene.camera.transform.position.x + Constants.SCREEN_WIDTH) { 54 | Transform oldTransform = gameObject.transform; 55 | gameObject.transform = new Transform(oldTransform.position); 56 | gameObject.transform.position = new Vector2(-camera.transform.position.x + gameObject.transform.position.x, -camera.transform.position.y + gameObject.transform.position.y); 57 | gameObject.draw(g2); 58 | gameObject.transform = oldTransform; 59 | } 60 | } else { 61 | gameObject.draw(g2); 62 | } 63 | } 64 | zIndex++; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/com/jade/jade/Transform.java: -------------------------------------------------------------------------------- 1 | package com.jade.jade; 2 | 3 | import com.jade.dataStructures.JString; 4 | import com.jade.dataStructures.Vector2; 5 | import com.jade.file.Parser; 6 | import com.jade.file.Serialize; 7 | 8 | public class Transform extends Serialize { 9 | public Vector2 position; 10 | 11 | public Transform(Vector2 position) { 12 | this.position = position; 13 | } 14 | 15 | @Override 16 | public String serialize(int tabSize) { 17 | StringBuilder builder = JString.getBuilder(); 18 | 19 | builder.append(beginObjectProperty("Transform", tabSize)); 20 | builder.append(position.serialize(tabSize + 1) + addEnding(true, false)); 21 | builder.append(closeObjectProperty(tabSize)); 22 | 23 | return builder.toString(); 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return "Vec2 (" + position.x + ", " + position.y + ")"; 29 | } 30 | 31 | public Transform clone() { 32 | return new Transform(new Vector2(this.position.x, this.position.y)); 33 | } 34 | 35 | public static Transform deserialize() { 36 | Parser.skipWhiteSpace(); 37 | String transformTitle = Parser.parseString(); 38 | Parser.checkString("Transform", transformTitle); 39 | Parser.consume(':'); 40 | Parser.consume('{'); 41 | 42 | Vector2 position = Vector2.deserialize(); 43 | 44 | Parser.consume('}'); 45 | 46 | return new Transform(position); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/com/jade/main/Constants.java: -------------------------------------------------------------------------------- 1 | package com.jade.main; 2 | 3 | public class Constants { 4 | // ======================================================= 5 | // Physics Constants 6 | // ======================================================= 7 | public static final int GRAVITY = 2850; 8 | public static final int TERMINAL_VELOCITY = 1900; 9 | 10 | // ======================================================= 11 | // Window properties 12 | // ======================================================= 13 | public static final int SCREEN_WIDTH = 1280; 14 | public static final int SCREEN_HEIGHT = 720; 15 | public static final String SCREEN_TITLE = "Geometry Dash"; 16 | 17 | // ======================================================= 18 | // Background image constants 19 | // ======================================================= 20 | public static final int BG_WIDTH = 512; 21 | public static final int BG_HEIGHT = 512; 22 | public static final int GROUND_BG_WIDTH = 256; 23 | public static final int GROUND_BG_HEIGHT = 256; 24 | 25 | // ======================================================= 26 | // Player constants 27 | // ======================================================= 28 | public static final int PLR_WIDTH = 41; 29 | public static final int PLR_HEIGHT = 41; 30 | public static final int SPEED = 395; 31 | public static final int JUMP_FORCE = -650; 32 | public static final int FLY_FORCE = -45; 33 | public static final int FLY_TERMINAL_VELOCITY = 500; 34 | 35 | // ======================================================= 36 | // Tile constants 37 | // ======================================================= 38 | public static final int TILE_WIDTH = 42; 39 | public static final int ONE_TENTH_TILE_WIDTH = 4; 40 | public static final int GRID_WIDTH = 42; 41 | public static final int GRID_HEIGHT = 42; 42 | 43 | 44 | // ======================================================= 45 | // Camera constants 46 | // ======================================================= 47 | public static final int GROUND_HEIGHT = 3 * Constants.TILE_WIDTH; 48 | public static final int CAMERA_OFFSET_Y = -385; 49 | public static final int CAMERA_OFFSET_X = -400; 50 | public static final int CAMERA_BOX_TOP_Y = 250; 51 | public static final int CAMERA_BOX_BOTTOM_Y = 450; 52 | 53 | // ======================================================= 54 | // Level editor UI constants 55 | // ======================================================= 56 | public static final int BUTTON_WIDTH = 60; 57 | public static final int BIG_BUTTON_WIDTH = 90; 58 | public static final int MENU_CONTAINER_Y = 535; 59 | public static final int BUTTON_OFFSET_X = 400; 60 | public static final int BUTTON_OFFSET_Y = 560; 61 | public static final int BUTTON_HORIZONTAL_SPACING = 10; 62 | 63 | public static final int TAB_WIDTH = 75; 64 | public static final int TAB_HEIGHT = 38; 65 | public static final int TAB_OFFSET_X = 380; 66 | public static final int TAB_OFFSET_Y = 497; 67 | public static final int TAB_HORIZONTAL_SPACING = 10; 68 | 69 | // ======================================================= 70 | // Miscellaneous 71 | // ======================================================= 72 | public static final char[] WHITESPACE = {'\n', ' ', '\t', '\r'}; 73 | } 74 | -------------------------------------------------------------------------------- /src/com/jade/main/KL.java: -------------------------------------------------------------------------------- 1 | package com.jade.main; 2 | 3 | import java.awt.event.KeyAdapter; 4 | import java.awt.event.KeyEvent; 5 | import java.awt.event.KeyListener; 6 | 7 | public class KL extends KeyAdapter implements KeyListener { 8 | private boolean keyPressed[] = new boolean[128]; 9 | 10 | @Override 11 | public void keyTyped(KeyEvent keyEvent) { } 12 | 13 | @Override 14 | public void keyPressed(KeyEvent keyEvent) { 15 | keyPressed[keyEvent.getKeyCode()] = true; 16 | } 17 | 18 | @Override 19 | public void keyReleased(KeyEvent keyEvent) { 20 | keyPressed[keyEvent.getKeyCode()] = false; 21 | } 22 | 23 | public boolean isKeyPressed(int keyCode) { 24 | return keyPressed[keyCode]; 25 | } 26 | } -------------------------------------------------------------------------------- /src/com/jade/main/LevelEditorScene.java: -------------------------------------------------------------------------------- 1 | package com.jade.main; 2 | 3 | import java.awt.*; 4 | import java.awt.event.KeyEvent; 5 | import java.io.*; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Random; 9 | import java.util.zip.ZipEntry; 10 | import java.util.zip.ZipFile; 11 | import java.util.zip.ZipOutputStream; 12 | 13 | import com.jade.components.BoxBounds; 14 | import com.jade.components.Sprite; 15 | import com.jade.components.TriangleBounds; 16 | import com.jade.dataStructures.*; 17 | import com.jade.jade.Camera; 18 | import com.jade.jade.GameObject; 19 | import com.jade.jade.Transform; 20 | import com.jade.scripts.*; 21 | 22 | import com.jade.file.Parser; 23 | import com.jade.jade.Renderer; 24 | import com.jade.ui.Grid; 25 | import com.jade.ui.MainContainer; 26 | 27 | public class LevelEditorScene extends Scene { 28 | private List gameObjects; 29 | public Renderer renderingEngine; 30 | 31 | private static LevelEditorScene scene; 32 | private GameObject player = null; 33 | private Grid grid = new Grid(Constants.GRID_WIDTH); 34 | 35 | private List selectedObjects = new ArrayList<>(); 36 | private List objsToRemove = new ArrayList<>(); 37 | private List objsToAdd = new ArrayList<>(); 38 | 39 | public static GameObject cursor; 40 | private GameObject ground; 41 | 42 | SpecieManager specieManager; 43 | Genome currentGenome; 44 | float evaluation; 45 | float fitness; 46 | 47 | private float debounceTime = 0.01f; 48 | private float debounceLeft = 0.1f; 49 | 50 | public LevelEditorScene(String name) { 51 | super.Scene(name); 52 | gameObjects = new ArrayList<>(); 53 | 54 | camera = new Camera(new Transform(new Vector2(0.0f, 0.0f))); 55 | renderingEngine = new Renderer(camera); 56 | } 57 | 58 | @Override 59 | public void init() { 60 | specieManager = new SpecieManager(3, 1); 61 | requestNext(); 62 | 63 | initPlayer(); 64 | initBackgrounds(); 65 | LevelEditorScene.cursor = new GameObject("Cursor", new Transform(new Vector2())); 66 | cursor.addComponent(new CursorScript()); 67 | cursor.addComponent(new EditorKeyShortcuts()); 68 | 69 | Sprite menuContainerBg = new Sprite("assets/ui/menuContainerBackground.png", 1280, 180); 70 | GameObject mainContainer = new GameObject("Nonserialize", new Transform(new Vector2(0, Constants.MENU_CONTAINER_Y))); 71 | mainContainer.setUi(true); 72 | mainContainer.addComponent(menuContainerBg); 73 | mainContainer.addComponent(new MainContainer()); 74 | mainContainer.setZIndex(100); 75 | addGameObject(mainContainer); 76 | 77 | GameObject cameraHelper = new GameObject("Nonserialize", new Transform(new Vector2(0.0f, 0.0f))); 78 | cameraHelper.addComponent(new CameraControls()); 79 | addGameObject(cameraHelper); 80 | camera.transform.position = new Vector2(player.transform.position.x + Constants.CAMERA_OFFSET_X, player.transform.position.y + Constants.CAMERA_OFFSET_Y + 30); 81 | grid.start(); 82 | importLevel("levelOutput"); 83 | } 84 | 85 | public void requestNext() { 86 | fitness = 0.0f; 87 | Genome nextAi = specieManager.nextGenome(); 88 | currentGenome = nextAi; 89 | } 90 | 91 | public void safeAddGameObject(GameObject go) { 92 | objsToAdd.add(go); 93 | } 94 | 95 | private void addGameObject(GameObject gameObj) { 96 | gameObjects.add(gameObj); 97 | renderingEngine.submit(gameObj); 98 | gameObj.parent = null; 99 | } 100 | 101 | public void safeRemoveGameObject(GameObject go) { 102 | objsToRemove.add(go); 103 | } 104 | 105 | private void removeGameObject(GameObject go) { 106 | gameObjects.remove(go); 107 | renderingEngine.remove(go); 108 | } 109 | 110 | public static LevelEditorScene getScene() { 111 | if (LevelEditorScene.scene == null) { 112 | LevelEditorScene.scene = new LevelEditorScene("Level Editor"); 113 | } 114 | 115 | return LevelEditorScene.scene; 116 | } 117 | 118 | public static void deleteScene() { 119 | LevelEditorScene.cursor = null; 120 | LevelEditorScene.scene = null; 121 | } 122 | 123 | public float eval(float f1, float f2, boolean shouldBeTrue) { 124 | currentGenome.getInputs().get(0).setValue(f1); 125 | currentGenome.getInputs().get(1).setValue(f2); 126 | currentGenome.getInputs().get(2).setValue(1.0f); 127 | currentGenome.run(); 128 | while (!currentGenome.isReady) {} 129 | List output = currentGenome.outputs; 130 | if (shouldBeTrue) { 131 | return 1.0f - output.get(0).getValue(); 132 | } else { 133 | return output.get(0).getValue(); 134 | } 135 | } 136 | 137 | public void resetGenome() { 138 | for (NodeGene gene : currentGenome.getNodeGenes()) { 139 | gene.setValue(0.0f); 140 | } 141 | } 142 | 143 | public void fullEval() { 144 | fitness = 0.0f; 145 | float error = 0.0f; 146 | error += eval(0.0f, 0.0f, false); 147 | error += eval(0.0f, 1.0f, true); 148 | error += eval(1.0f, 0.0f, true); 149 | error += eval(1.0f, 1.0f, false); 150 | fitness = 4.0f - error; 151 | fitness *= fitness * fitness; 152 | currentGenome.setFitness(fitness); 153 | resetGenome(); 154 | } 155 | 156 | @Override 157 | public void update(double dt) { 158 | debounceLeft -= dt; 159 | if (debounceLeft < 0.0f) { 160 | if (Window.keyListener.isKeyPressed(KeyEvent.VK_SPACE)) { 161 | fullEval(); 162 | requestNext(); 163 | } else if (Window.keyListener.isKeyPressed(KeyEvent.VK_ENTER)) { 164 | fullEval(); 165 | } 166 | debounceLeft = debounceTime; 167 | } 168 | 169 | cursor.update(dt); 170 | grid.update(dt); 171 | 172 | for (GameObject go : gameObjects) { 173 | if (go.transform.position.x > camera.transform.position.x - Constants.TILE_WIDTH * 2 && go.transform.position.x < camera.transform.position.x + Constants.SCREEN_WIDTH); 174 | go.update(dt); 175 | } 176 | 177 | ground.transform.position.x = camera.transform.position.x; 178 | 179 | if (objsToRemove.size() > 0) { 180 | for (GameObject obj : objsToRemove) { 181 | removeGameObject(obj); 182 | } 183 | objsToRemove.clear(); 184 | } 185 | 186 | if (objsToAdd.size() > 0) { 187 | for (GameObject go : objsToAdd) { 188 | addGameObject(go); 189 | } 190 | objsToAdd.clear(); 191 | } 192 | 193 | if (Window.keyListener.isKeyPressed(KeyEvent.VK_F1)) { 194 | export("levelOutput"); 195 | } else if (Window.keyListener.isKeyPressed(KeyEvent.VK_F2)) { 196 | export("levelOutput"); 197 | Window.getWindow().changeScene(1); 198 | } 199 | } 200 | 201 | @Override 202 | public void draw(Graphics2D g2) { 203 | grid.draw(g2); 204 | renderingEngine.draw(g2); 205 | cursor.getComponent(SnapToGrid.class).draw(g2); 206 | cursor.getComponent(CursorScript.class).draw(g2);; 207 | specieManager.draw(g2, (int)(fitness * 10000.0f), evaluation); 208 | currentGenome.draw(g2); 209 | } 210 | 211 | public GameObject raycastMouseclick(int x, int y) { 212 | float newX = (Window.mouseListener.x + camera.transform.position.x + Window.mouseListener.dx); 213 | float newY = (Window.mouseListener.y + camera.transform.position.y + Window.mouseListener.dy); 214 | 215 | for (GameObject go : gameObjects) { 216 | if (go.getComponent(BoxBounds.class) != null && go.getComponent(BoxBounds.class).pointInSquare(newX, newY) && go.name.compareTo("Nonserialize") != 0) { 217 | return go; 218 | } else if (go.getComponent(TriangleBounds.class) != null && go.getComponent(TriangleBounds.class).pointInTriangle(newX, newY)) { 219 | return go; 220 | } 221 | } 222 | 223 | return null; 224 | } 225 | 226 | public List addBoxSelect(float x, float y, float width, float height) { 227 | float x0 = x + camera.transform.position.x; 228 | float y0 = y + camera.transform.position.y; 229 | 230 | List objs = new ArrayList<>(); 231 | for (GameObject go : gameObjects) { 232 | if (go.getComponent(BoxBounds.class) != null && go.getComponent(BoxBounds.class).isContainedInRectangle(x0, y0, width ,height) && go.name.compareTo("Nonserialize") != 0) { 233 | objs.add(go); 234 | selectedObjects.add(go); 235 | } else if (go.getComponent(TriangleBounds.class) != null && go.getComponent(TriangleBounds.class).isContainedInRectangle(x0, y0, width, height)) { 236 | objs.add(go); 237 | selectedObjects.add(go); 238 | } 239 | } 240 | 241 | return objs; 242 | } 243 | 244 | public List duplicateSelected() { 245 | List objs = new ArrayList<>(); 246 | for (GameObject go : selectedObjects) { 247 | GameObject clone = go.clone(); 248 | objs.add(go); 249 | safeAddGameObject(clone); 250 | 251 | if (go.getComponent(BoxBounds.class) != null) go.getComponent(BoxBounds.class).isSelected = false; 252 | else if (go.getComponent(TriangleBounds.class) != null) go.getComponent(TriangleBounds.class).isSelected = false; 253 | } 254 | 255 | selectedObjects.clear(); 256 | selectedObjects.addAll(objs); 257 | return objs; 258 | } 259 | 260 | public void deleteSelected() { 261 | for (GameObject go : selectedObjects) { 262 | removeGameObject(go); 263 | } 264 | selectedObjects.clear(); 265 | } 266 | 267 | public void addOrRemoveSelectedGameObject(GameObject go) { 268 | TriangleBounds tBounds = go.getComponent(TriangleBounds.class); 269 | BoxBounds bounds = go.getComponent(BoxBounds.class); 270 | if (selectedObjects.contains(go)) { 271 | selectedObjects.remove(go); 272 | if (bounds != null) bounds.isSelected = false; 273 | else if (tBounds != null) tBounds.isSelected = false; 274 | } 275 | else { 276 | selectedObjects.add(go); 277 | if (bounds != null) bounds.isSelected = true; 278 | else if (tBounds != null) tBounds.isSelected = true; 279 | } 280 | } 281 | 282 | public void rotateSelected(double angle) { 283 | for (GameObject obj : this.selectedObjects) { 284 | BoxBounds bBounds = obj.getComponent(BoxBounds.class); 285 | TriangleBounds tBounds= obj.getComponent(TriangleBounds.class); 286 | 287 | if (bBounds != null) { 288 | bBounds.angle += angle; 289 | } else if (tBounds != null) { 290 | tBounds.angle += angle; 291 | } 292 | } 293 | } 294 | 295 | public List getSelectedGameObjects() { return this.selectedObjects; } 296 | 297 | private void importLevel(String filename) { 298 | File tmp = new File("levels/" + filename + ".zip"); 299 | if (!tmp.exists()) return; 300 | 301 | try { 302 | ZipFile zipFile = new ZipFile("levels/" + filename + ".zip"); 303 | ZipEntry jsonFile = zipFile.getEntry(filename + ".json"); 304 | InputStream stream = zipFile.getInputStream(jsonFile); 305 | byte[] bytes = stream.readAllBytes(); 306 | Parser.init(0, bytes); 307 | GameObject go = Parser.getNextGameObject(); 308 | 309 | while (go != null) { 310 | addGameObject(go); 311 | go = Parser.getNextGameObject(); 312 | } 313 | 314 | } catch (IOException e) { 315 | e.printStackTrace(); 316 | System.exit(-1); 317 | } 318 | 319 | } 320 | 321 | private void export(String filename) { 322 | try { 323 | FileOutputStream fos = new FileOutputStream("levels/" + filename + ".zip"); 324 | ZipOutputStream zos = new ZipOutputStream(fos); 325 | 326 | zos.putNextEntry(new ZipEntry(filename + ".json")); 327 | 328 | int i = 0; 329 | for (GameObject go : gameObjects) { 330 | if (go.name == "Nonserialize") { 331 | i++; 332 | continue; 333 | } 334 | zos.write(go.serialize(0).getBytes()); 335 | if (i != gameObjects.size() - 1) 336 | zos.write(",\n".getBytes()); 337 | i++; 338 | } 339 | 340 | zos.closeEntry(); 341 | zos.close(); 342 | fos.close(); 343 | } catch(IOException e) { 344 | e.printStackTrace(); 345 | System.exit(-1); 346 | } 347 | } 348 | 349 | private void initPlayer() { 350 | Sprite playerLayerOneSpritesheet = new Sprite("assets/player/layerOne.png", true, 42, 42, 13, 2, 13 * 5); 351 | playerLayerOneSpritesheet.loadSpritesheet(); 352 | 353 | Sprite playerLayerTwoSpritesheet = new Sprite("assets/player/layerTwo.png", true, 42, 42, 13, 2, 13 * 5); 354 | playerLayerTwoSpritesheet.loadSpritesheet(); 355 | 356 | Sprite playerLayerThreeSpritesheet = new Sprite("assets/player/layerThree.png", true, 42, 42, 13, 2, 13 * 5); 357 | playerLayerThreeSpritesheet.loadSpritesheet(); 358 | 359 | Sprite spaceship = new Sprite("assets/player/spaceship.png", true, 42, 42, 1, 2, 1); 360 | spaceship.loadSpritesheet(); 361 | 362 | int playerImg = 20; 363 | player = new GameObject("Nonserialize", new Transform(new Vector2(200.0f, 0))); 364 | BoxBounds bounds = new BoxBounds(Constants.PLR_WIDTH, Constants.PLR_HEIGHT, false, false); 365 | player.addComponent(bounds); 366 | player.addComponent(new Player(playerLayerOneSpritesheet.sprites.get(playerImg).subImg.image, playerLayerTwoSpritesheet.sprites.get(playerImg).subImg.image, 367 | playerLayerThreeSpritesheet.sprites.get(playerImg).subImg.image, Color.PINK, Color.ORANGE, spaceship.sprites.get(0).subImg.image, false)); 368 | addGameObject(player); 369 | } 370 | 371 | private void initBackgrounds() { 372 | ground = new GameObject("Nonserialize", new Transform(new Vector2(0.0f, Constants.GROUND_HEIGHT))); 373 | ground.addComponent(new BoxBounds(Constants.SCREEN_WIDTH, 300.0f, false, false)); 374 | Ground groundScript = new Ground(player); 375 | ground.addComponent(groundScript); 376 | addGameObject(ground); 377 | 378 | int numBackgrounds = 7; 379 | GameObject[] backgrounds = new GameObject[numBackgrounds]; 380 | GameObject[] groundBgs = new GameObject[numBackgrounds]; 381 | for (int i=0; i < numBackgrounds; i++) { 382 | int x = i * Constants.BG_WIDTH; 383 | int y = 0; 384 | GameObject go = new GameObject("Nonserialize", new Transform(new Vector2(x, y))); 385 | go.setUi(true); 386 | ParallaxBackground bg = new ParallaxBackground(Constants.BG_WIDTH, Constants.BG_HEIGHT, "" + 387 | "assets/backgrounds/bg01.png", backgrounds, false, ground.getComponent(Ground.class), false); 388 | go.addComponent(bg); 389 | go.setZIndex(-100); 390 | backgrounds[i] = go; 391 | 392 | x = i * Constants.GROUND_BG_WIDTH; 393 | y = Constants.BG_HEIGHT; 394 | GameObject groundGo = new GameObject("Nonserialize", new Transform(new Vector2(x, y))); 395 | groundGo.setUi(true); 396 | ParallaxBackground groundBg = new ParallaxBackground(Constants.GROUND_BG_WIDTH, Constants.GROUND_BG_HEIGHT, 397 | "assets/grounds/ground01.png", groundBgs, false, ground.getComponent(Ground.class), true); 398 | groundGo.addComponent(groundBg); 399 | groundBgs[i] = groundGo; 400 | groundGo.setZIndex(-99); 401 | 402 | addGameObject(go); 403 | addGameObject(groundGo); 404 | } 405 | } 406 | } 407 | -------------------------------------------------------------------------------- /src/com/jade/main/LevelScene.java: -------------------------------------------------------------------------------- 1 | package com.jade.main; 2 | 3 | import java.awt.*; 4 | import java.awt.event.KeyEvent; 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | import java.util.List; 11 | import java.util.zip.ZipEntry; 12 | import java.util.zip.ZipFile; 13 | 14 | import com.jade.components.BoxBounds; 15 | import com.jade.components.Sprite; 16 | import com.jade.components.TriangleBounds; 17 | import com.jade.dataStructures.Genome; 18 | import com.jade.dataStructures.NodeGene; 19 | import com.jade.dataStructures.SpecieManager; 20 | import com.jade.dataStructures.Vector2; 21 | import com.jade.jade.Camera; 22 | import com.jade.jade.GameObject; 23 | import com.jade.jade.Transform; 24 | import com.jade.scripts.Ground; 25 | import com.jade.scripts.Music; 26 | import com.jade.scripts.ParallaxBackground; 27 | import com.jade.scripts.Player; 28 | 29 | import com.jade.file.Parser; 30 | import com.jade.jade.Renderer; 31 | 32 | public class LevelScene extends Scene { 33 | public List gameObjects; 34 | public Renderer renderingEngine; 35 | 36 | private static LevelScene scene = null; 37 | private Music stereoMadness = null; 38 | 39 | public GameObject player; 40 | public Sprite playerLayerOneSpritesheet, playerLayerTwoSpritesheet, playerLayerThreeSpritesheet, spaceship; 41 | 42 | public boolean testingAi = true; 43 | public SpecieManager specieManager; 44 | public Genome currentGenome = null; 45 | public float distance = 0.0f; 46 | float evaluation = 0.0f; 47 | 48 | public LevelScene(String name) { 49 | super.Scene(name); 50 | gameObjects = new ArrayList<>(); 51 | 52 | camera = new Camera(new Transform(new Vector2(0.0f, 0.0f))); 53 | renderingEngine = new Renderer(camera); 54 | } 55 | 56 | public static LevelScene getScene() { 57 | if (LevelScene.scene == null) { 58 | LevelScene.scene = new LevelScene("Level 1"); 59 | } 60 | 61 | return LevelScene.scene; 62 | } 63 | 64 | public static void deleteScene() { 65 | if (LevelScene.getScene() != null && LevelScene.getScene().stereoMadness != null) 66 | LevelScene.getScene().stereoMadness.stop(); 67 | LevelScene.scene = null; 68 | } 69 | 70 | @Override 71 | public void init() { 72 | initPlayer(); 73 | if (testingAi) { 74 | specieManager = new SpecieManager(81, 1); 75 | resetAi(); 76 | } 77 | initBackgrounds(); 78 | importLevel("levelOutput"); 79 | Collections.sort(gameObjects); 80 | if (!testingAi) 81 | stereoMadness = new Music("assets/stereoMadness.wav"); 82 | } 83 | 84 | public void resetAi() { 85 | if (currentGenome != null) 86 | currentGenome.setFitness(distance); 87 | Genome nextAi = specieManager.nextGenome(); 88 | distance = 0; 89 | currentGenome = nextAi; 90 | player.transform = new Transform(new Vector2(-90.0f, 0)); 91 | } 92 | 93 | public void addGameObject(GameObject gameObj) { 94 | gameObjects.add(gameObj); 95 | renderingEngine.submit(gameObj); 96 | gameObj.parent = null; 97 | } 98 | 99 | public List getAllObjectsInView(GameObject me) { 100 | List res = new ArrayList<>(); 101 | Vector2 startPos = new Vector2((((float)(Math.floor(camera.transform.position.x / 42.0f)) + 9) * 42.0f) + 3, 102 | (((float)(Math.floor(camera.transform.position.y / 42.0f)) + 7) * 42.0f) + 3); 103 | float initialX = startPos.x; 104 | BoxBounds boundChecker = new BoxBounds(36, 36, 0, false, false, true); 105 | 106 | for (int i=0; i < 8; i++) { 107 | for (int j=0; j < 10; j++) { 108 | boolean added = false; 109 | if (startPos.y > Constants.CAMERA_OFFSET_Y + 500) { 110 | added = true; 111 | res.add(1); 112 | } 113 | 114 | if (!added) { 115 | for (GameObject go : gameObjects) { 116 | if (go.name.compareTo("Nonserialize") == 0 || go.name.compareTo("Player") == 0 117 | || go.name.compareTo("Ground") == 0 || go.name.compareTo("Background") == 0) continue; 118 | if (go.transform.position.x > startPos.x + 42 || go.transform.position.y > startPos.y + 42) 119 | continue; 120 | 121 | if (go.getComponent(BoxBounds.class) != null || go.getComponent(TriangleBounds.class) != null) { 122 | if (go.getComponent(BoxBounds.class) != null) { 123 | BoxBounds bounds = go.getComponent(BoxBounds.class); 124 | if (go.transform.position.x + bounds.width + bounds.xBuffer < startPos.x) continue; 125 | else if (go.transform.position.y + bounds.height + bounds.yBuffer < startPos.y) 126 | continue; 127 | if (bounds.isColliding(boundChecker, startPos)) { 128 | if (go.getComponent(Genome.class) != null) 129 | res.add(3); 130 | else if (bounds.isDeathBox) 131 | res.add(2); 132 | else if (bounds.canCollide) 133 | res.add(1); 134 | else 135 | res.add(0); 136 | added = true; 137 | break; 138 | } 139 | } else { 140 | TriangleBounds tBounds = go.getComponent(TriangleBounds.class); 141 | if (go.transform.position.x + tBounds.base + tBounds.xBuffer < startPos.x) continue; 142 | else if (go.transform.position.y + tBounds.height + tBounds.yBuffer < startPos.y) 143 | continue; 144 | if (tBounds.isColliding(boundChecker, startPos)) { 145 | res.add(2); 146 | added = true; 147 | break; 148 | } 149 | } 150 | } 151 | } 152 | } 153 | if (!added) res.add(0); 154 | startPos.x += 42; 155 | } 156 | startPos.x = initialX; 157 | startPos.y += 42; 158 | } 159 | 160 | for (int i=0; i < res.size(); i++) { 161 | if (i % 10 == 0) System.out.print("\n"); 162 | System.out.print("" + res.get(i) + " "); 163 | } 164 | System.out.println(); 165 | return res; 166 | } 167 | 168 | private boolean intersection(BoxBounds b, Vector2 p1, Vector2 p2, double invX, double invY) { 169 | double tx1 = (b.parent.transform.position.x + b.xBuffer - p1.x) * invX; 170 | double tx2 = (b.parent.transform.position.x + b.xBuffer + b.width - p1.x) * invX; 171 | 172 | double tmin = Math.min(tx1, tx2); 173 | double tmax = Math.max(tx1, tx2); 174 | 175 | double ty1 = (b.parent.transform.position.y + b.yBuffer - p1.y) * invY; 176 | double ty2 = (b.parent.transform.position.y + b.yBuffer + b.height - p1.y) * invY; 177 | 178 | tmin = Math.max(tmin, Math.min(ty1, ty2)); 179 | tmax = Math.min(tmax, Math.max(ty1, ty2)); 180 | 181 | return tmax >= tmin; 182 | } 183 | 184 | @Override 185 | public void update(double dt) { 186 | if (Window.keyListener.isKeyPressed(KeyEvent.VK_SPACE)) return; 187 | 188 | distance += dt; 189 | player.update(dt); 190 | player.getComponent(BoxBounds.class).onGround = false; 191 | for (GameObject go : gameObjects) { 192 | if ((go.transform.position.x > camera.transform.position.x - Constants.TILE_WIDTH * 2 && go.transform.position.x < camera.transform.position.x + Constants.SCREEN_WIDTH) || 193 | go.getComponent(ParallaxBackground.class) != null) { 194 | go.update(dt); 195 | } 196 | } 197 | 198 | if (testingAi && currentGenome.isReady) { 199 | if (currentGenome.outputs.size() != 1) { 200 | System.out.println("UH OH: The output is not of size 1! It is size: " + currentGenome.outputs.size()); 201 | System.exit(-1); 202 | } 203 | evaluation = currentGenome.outputs.get(0).getValue(); 204 | if (currentGenome.outputs.get(0).getValue() > 0.85f) { 205 | player.getComponent(Player.class).jump(player.getComponent(BoxBounds.class)); 206 | } 207 | 208 | List objs = getAllObjectsInView(player); 209 | List inputs = currentGenome.getInputs(); 210 | if (objs.size() != inputs.size() - 1) { 211 | System.out.println("UH OH: The inputs and objects in view are different lengths!"); 212 | System.exit(-1); 213 | } 214 | for (int i=0; i < objs.size(); i++) { 215 | if (objs.get(i) == 0) { 216 | inputs.get(i).setValue(0.0f); 217 | } else if (objs.get(i) == 1) { 218 | inputs.get(i).setValue(0.3f); 219 | } else if (objs.get(i) == 2) { 220 | inputs.get(i).setValue(0.6f); 221 | } else if (objs.get(i) == 3) { 222 | inputs.get(i).setValue(0.0f); 223 | } 224 | } 225 | if (player.getComponent(Player.class).state == 0) { 226 | inputs.get(80).setValue(0.3f); 227 | } else { 228 | inputs.get(80).setValue(0.6f); 229 | } 230 | 231 | currentGenome.run(); 232 | } 233 | 234 | camera.transform.position.x = player.transform.position.x + Constants.CAMERA_OFFSET_X; 235 | 236 | float cameraDistanceY = player.transform.position.y - camera.transform.position.y; 237 | if (cameraDistanceY < Constants.CAMERA_BOX_TOP_Y || cameraDistanceY > Constants.CAMERA_BOX_BOTTOM_Y ) { 238 | if (cameraDistanceY < Constants.CAMERA_BOX_TOP_Y && player.getComponent(Player.class).state == 0) { 239 | camera.transform.position.y -= Constants.CAMERA_BOX_TOP_Y - cameraDistanceY; 240 | } else if (cameraDistanceY > Constants.CAMERA_BOX_BOTTOM_Y && player.getComponent(Player.class).state == 0){ 241 | camera.transform.position.y += cameraDistanceY - Constants.CAMERA_BOX_BOTTOM_Y; 242 | } 243 | } 244 | 245 | if (camera.transform.position.y > Constants.CAMERA_OFFSET_Y) { 246 | camera.transform.position.y = Constants.CAMERA_OFFSET_Y; 247 | } 248 | 249 | if (Window.keyListener.isKeyPressed(KeyEvent.VK_F1)) { 250 | Window.getWindow().changeScene(0); 251 | } 252 | } 253 | 254 | @Override 255 | public void draw(Graphics2D g2) { 256 | renderingEngine.draw(g2); 257 | specieManager.draw(g2, (int)(distance * 100), evaluation); 258 | currentGenome.draw(g2); 259 | 260 | 261 | // Vector2 startPos = new Vector2((((float)(Math.floor(player.transform.position.x / 42.0f)) + 1) * 42.0f) + 3, 262 | // (((float)(Math.floor(player.transform.position.y / 42.0f)) - 4) * 42.0f) + 3); 263 | // float initialX = startPos.x; 264 | // BoxBounds boundChecker = new BoxBounds(36, 36, 0, false, false, true); 265 | // 266 | // g2.setStroke(new BasicStroke(1.0f)); 267 | // for (int i=0; i < 8; i++) { 268 | // for (int j=0; j < 10; j++) { 269 | // g2.setColor(Color.GREEN); 270 | // g2.drawRect((int)(startPos.x - camera.transform.position.x), 271 | // (int)(startPos.y - camera.transform.position.y), (int)boundChecker.width, (int)boundChecker.height); 272 | // startPos.x += 42; 273 | // } 274 | // startPos.x = initialX; 275 | // startPos.y += 42; 276 | // } 277 | } 278 | 279 | private void importLevel(String filename) { 280 | File tmp = new File("levels/" + filename + ".zip"); 281 | if (!tmp.exists()) return; 282 | 283 | try { 284 | ZipFile zipFile = new ZipFile("levels/" + filename + ".zip"); 285 | ZipEntry jsonFile = zipFile.getEntry(filename + ".json"); 286 | InputStream stream = zipFile.getInputStream(jsonFile); 287 | byte[] bytes = stream.readAllBytes(); 288 | Parser.init(0, bytes); 289 | GameObject go = Parser.getNextGameObject(); 290 | 291 | while (go != null) { 292 | addGameObject(go); 293 | go = Parser.getNextGameObject(); 294 | } 295 | 296 | } catch (IOException e) { 297 | e.printStackTrace(); 298 | System.exit(-1); 299 | } 300 | 301 | } 302 | 303 | private void initPlayer() { 304 | playerLayerOneSpritesheet = new Sprite("assets/player/layerOne.png", true, 42, 42, 13, 2, 13 * 5); 305 | playerLayerOneSpritesheet.loadSpritesheet(); 306 | 307 | playerLayerTwoSpritesheet = new Sprite("assets/player/layerTwo.png", true, 42, 42, 13, 2, 13 * 5); 308 | playerLayerTwoSpritesheet.loadSpritesheet(); 309 | 310 | playerLayerThreeSpritesheet = new Sprite("assets/player/layerThree.png", true, 42, 42, 13, 2, 13 * 5); 311 | playerLayerThreeSpritesheet.loadSpritesheet(); 312 | 313 | spaceship = new Sprite("assets/player/spaceship.png", true, 42, 42, 1, 2, 1); 314 | spaceship.loadSpritesheet(); 315 | 316 | int playerImg = 20; 317 | player = new GameObject("Player", new Transform(new Vector2(-90.0f, 0))); 318 | BoxBounds bounds = new BoxBounds(Constants.PLR_WIDTH, Constants.PLR_HEIGHT, true, false); 319 | player.addComponent(bounds); 320 | player.setZIndex(1); 321 | player.addComponent(new Player(playerLayerOneSpritesheet.sprites.get(playerImg).subImg.image, playerLayerTwoSpritesheet.sprites.get(playerImg).subImg.image, 322 | playerLayerThreeSpritesheet.sprites.get(playerImg).subImg.image, Color.GREEN, Color.YELLOW, spaceship.sprites.get(0).subImg.image, true)); 323 | 324 | if (testingAi) 325 | player.getComponent(Player.class).isAi = true; 326 | renderingEngine.submit(player); 327 | } 328 | 329 | private void initBackgrounds() { 330 | Sprite blocks = new Sprite("assets/spritesheet.png", true, Constants.TILE_WIDTH, Constants.TILE_WIDTH, 6, 2, 12); 331 | blocks.loadSpritesheet(); 332 | 333 | Sprite spikes = new Sprite("assets/spikes.png", true, Constants.TILE_WIDTH, Constants.TILE_WIDTH, 6, 2, 4); 334 | spikes.loadSpritesheet(); 335 | 336 | Sprite bigSprites = new Sprite("assets/bigSprites.png", true, Constants.TILE_WIDTH * 2, Constants.TILE_WIDTH * 2, 2, 2, 2); 337 | bigSprites.loadSpritesheet(); 338 | 339 | Sprite smallBlocks = new Sprite("assets/smallBlocks.png", true, Constants.TILE_WIDTH, Constants.TILE_WIDTH, 6, 2, 1); 340 | smallBlocks.loadSpritesheet(); 341 | 342 | Sprite portal = new Sprite("assets/portal.png", true, 44, 85, 2, 2, 2); 343 | portal.loadSpritesheet(); 344 | 345 | GameObject ground = new GameObject("Ground", new Transform(new Vector2(0.0f, Constants.GROUND_HEIGHT))); 346 | ground.addComponent(new BoxBounds(Constants.SCREEN_WIDTH, 300.0f, true, false)); 347 | Ground groundScript = new Ground(player); 348 | ground.addComponent(groundScript); 349 | addGameObject(ground); 350 | 351 | int numBackgrounds = 7; 352 | GameObject[] backgrounds = new GameObject[numBackgrounds]; 353 | GameObject[] groundBgs = new GameObject[numBackgrounds]; 354 | for (int i=0; i < numBackgrounds; i++) { 355 | int x = i * Constants.BG_WIDTH; 356 | int y = 0; 357 | GameObject go = new GameObject("Background", new Transform(new Vector2(x, y))); 358 | go.setUi(true); 359 | ParallaxBackground bg = new ParallaxBackground(Constants.BG_WIDTH, Constants.BG_HEIGHT, 360 | "assets/backgrounds/bg01.png", backgrounds, true, ground.getComponent(Ground.class), false); 361 | go.addComponent(bg); 362 | go.setZIndex(-101); 363 | backgrounds[i] = go; 364 | 365 | x = i * Constants.GROUND_BG_WIDTH; 366 | y = Constants.BG_HEIGHT; 367 | GameObject groundGo = new GameObject("GroundBg", new Transform(new Vector2(x, y))); 368 | groundGo.setUi(true); 369 | ParallaxBackground groundBg = new ParallaxBackground(Constants.GROUND_BG_WIDTH, Constants.GROUND_BG_HEIGHT, 370 | "assets/grounds/ground01.png", groundBgs, true, ground.getComponent(Ground.class), true); 371 | groundGo.addComponent(groundBg); 372 | groundGo.setZIndex(-100); 373 | groundBgs[i] = groundGo; 374 | 375 | addGameObject(go); 376 | addGameObject(groundGo); 377 | } 378 | } 379 | } 380 | -------------------------------------------------------------------------------- /src/com/jade/main/ML.java: -------------------------------------------------------------------------------- 1 | package com.jade.main; 2 | 3 | import java.awt.event.MouseAdapter; 4 | import java.awt.event.MouseEvent; 5 | 6 | public class ML extends MouseAdapter { 7 | 8 | public boolean mousePressed = false; 9 | public boolean mouseDragged = false; 10 | public int mouseButton = -1; 11 | public float x = -1.0f, y = -1.0f; 12 | public float dx = -1.0f, dy = -1.0f; 13 | 14 | @Override 15 | public void mouseClicked(MouseEvent mouseEvent) { 16 | 17 | } 18 | 19 | @Override 20 | public void mousePressed(MouseEvent mouseEvent) { 21 | this.mousePressed = true; 22 | this.mouseButton = mouseEvent.getButton(); 23 | } 24 | 25 | @Override 26 | public void mouseReleased(MouseEvent mouseEvent) { 27 | this.mouseDragged = false; 28 | this.mousePressed = false; 29 | this.dx = 0; 30 | this.dy = 0; 31 | } 32 | 33 | @Override 34 | public void mouseEntered(MouseEvent mouseEvent) { 35 | } 36 | 37 | @Override 38 | public void mouseExited(MouseEvent mouseEvent) { 39 | 40 | } 41 | 42 | @Override 43 | public void mouseMoved(MouseEvent mouseEvent) { 44 | this.x = mouseEvent.getX(); 45 | this.y = mouseEvent.getY(); 46 | } 47 | 48 | @Override 49 | public void mouseDragged(MouseEvent mouseEvent) { 50 | this.mouseDragged = true; 51 | this.dx = mouseEvent.getX() - this.x; 52 | this.dy = mouseEvent.getY() - this.y; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/com/jade/main/Scene.java: -------------------------------------------------------------------------------- 1 | package com.jade.main; 2 | 3 | import com.jade.jade.Camera; 4 | 5 | import java.awt.Graphics2D; 6 | 7 | public abstract class Scene { 8 | String name; 9 | public static Camera camera; 10 | 11 | public void Scene(String name) { 12 | this.name = name; 13 | } 14 | 15 | public abstract void init(); 16 | public abstract void update(double dt); 17 | public abstract void draw(Graphics2D g2); 18 | } 19 | -------------------------------------------------------------------------------- /src/com/jade/main/Time.java: -------------------------------------------------------------------------------- 1 | package com.jade.main; 2 | 3 | public class Time { 4 | public static double timeStarted = System.nanoTime(); 5 | 6 | public static double getTime() { 7 | return (System.nanoTime() - timeStarted) * 1E-9; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/com/jade/main/Window.java: -------------------------------------------------------------------------------- 1 | package com.jade.main; 2 | 3 | import com.jade.jade.AssetPool; 4 | 5 | import javax.swing.JFrame; 6 | 7 | import java.awt.Graphics2D; 8 | import java.awt.Color; 9 | import java.awt.Graphics; 10 | import java.awt.Dimension; 11 | import java.awt.Image; 12 | import java.awt.RenderingHints; 13 | import java.awt.event.KeyEvent; 14 | 15 | public class Window extends JFrame implements Runnable { 16 | 17 | public static Graphics graphics; 18 | public static KL keyListener = new KL(); 19 | public static ML mouseListener = new ML(); 20 | public static boolean isEditing = false; 21 | boolean isRunning = true; 22 | 23 | Color[] colors = { 24 | new Color(15.0f / 255.0f, 98.0f / 255.0f, 212.0f / 255.0f, 1.0f), // BLUE 25 | new Color(140.0f / 255.0f, 29.0f / 255.0f, 209.0f / 255.0f, 1.0f), // PURPLE 26 | new Color(209.0f / 255.0f, 29.0f / 255.0f, 179.0f / 255.0f, 1.0f), // PINK 27 | new Color(181.0f / 255.0f, 31.0f / 255.0f, 31.0f / 255.0f, 1.0f), // RED 28 | new Color(237.0f / 255.0f, 132.0f / 255.0f, 40.0f / 255.0f, 1.0f), // ORANGE 29 | new Color(247.0f / 255.0f, 229.0f / 255.0f, 64.0f / 255.0f, 1.0f), // YELLOW 30 | new Color(44.0f / 255.0f, 163.0f / 255.0f, 23.0f / 255.0f, 1.0f), // GREEN 31 | new Color(15.0f / 255.0f, 209.0f / 255.0f, 193.0f / 255.0f, 1.0f), // CYAN 32 | }; 33 | 34 | float ogSteps = 300.0f; 35 | int currentColor = 0; 36 | int nextColor = 1; 37 | float steps = ogSteps; 38 | float blueStepAmount = 0.0f; 39 | float redStepAmount = 0.0f; 40 | float greenStepAmount = 0.0f; 41 | public Color bgColor = colors[currentColor]; 42 | public Color groundColor = new Color(28.0f / 255.0f, 70.0f / 255.0f, 148.0f / 255.0f, 1.0f); 43 | 44 | private static Window window = null; 45 | private boolean isPaused = false; 46 | private float debounce = 0.1f; 47 | private float debounceLeft = 0.1f; 48 | private int currentScene = 0; 49 | 50 | public Graphics doubleBuffer = null; 51 | Image doubleBufferImage = null; 52 | public static Scene scene; 53 | 54 | public Window() { 55 | this.setSize(Constants.SCREEN_WIDTH, Constants.SCREEN_HEIGHT); 56 | this.setTitle(Constants.SCREEN_TITLE); 57 | this.setResizable(false); 58 | this.setVisible(true); 59 | this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 60 | this.addKeyListener(Window.keyListener); 61 | this.addMouseListener(Window.mouseListener); 62 | this.addMouseMotionListener(Window.mouseListener); 63 | this.setLocationRelativeTo(null); 64 | } 65 | 66 | public static Window getWindow() { 67 | if (Window.window == null) { 68 | Window.window = new Window(); 69 | } 70 | 71 | return Window.window; 72 | } 73 | 74 | public void changeScene(int scene) { 75 | AssetPool.clearSprites(); 76 | LevelEditorScene.deleteScene(); 77 | LevelScene.deleteScene(); 78 | currentScene = scene; 79 | 80 | switch(scene) { 81 | case 0: 82 | Window.isEditing = true; 83 | Window.scene = LevelEditorScene.getScene(); 84 | Window.scene.init(); 85 | break; 86 | case 1: 87 | Window.isEditing = false; 88 | Window.scene = LevelScene.getScene(); 89 | Window.scene.init(); 90 | break; 91 | default: 92 | System.out.println("UNKNOWN SCENE!!"); 93 | Window.scene = null; 94 | break; 95 | } 96 | } 97 | 98 | public void init() { 99 | changeScene(0); 100 | Window.graphics = getGraphics(); 101 | } 102 | 103 | public void pause() { 104 | isPaused = true; 105 | } 106 | 107 | public void update(double dt) { 108 | debounceLeft -= dt; 109 | if (isPaused) { 110 | if (keyListener.isKeyPressed(KeyEvent.VK_ENTER) && debounceLeft < 0) { 111 | draw(getGraphics()); 112 | debounceLeft = debounce; 113 | Window.scene.update(0.01); 114 | } else if (keyListener.isKeyPressed(KeyEvent.VK_ESCAPE)) { 115 | isPaused = false; 116 | } 117 | } else { 118 | Window.scene.update(dt); 119 | if (isPaused) return; 120 | draw(getGraphics()); 121 | if (keyListener.isKeyPressed(KeyEvent.VK_F5)) { 122 | isPaused = true; 123 | } 124 | } 125 | } 126 | 127 | public void draw(Graphics g) { 128 | Dimension d = getSize(); 129 | if (doubleBufferImage == null) { 130 | doubleBufferImage = createImage(d.width, d.height); 131 | doubleBuffer = doubleBufferImage.getGraphics(); 132 | } 133 | renderOffscreen(doubleBufferImage.getGraphics()); 134 | Graphics2D g2 = (Graphics2D)g; 135 | g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 136 | RenderingHints.VALUE_ANTIALIAS_ON); 137 | 138 | if (currentScene == 0) 139 | g.drawImage(doubleBufferImage, 0, 0, null); 140 | else 141 | g.drawImage(doubleBufferImage, -300, -200, Constants.SCREEN_WIDTH + 600, Constants.SCREEN_HEIGHT + 400, null); 142 | } 143 | 144 | public void renderOffscreen(Graphics g) { 145 | Graphics2D g2 = (Graphics2D) g; 146 | g.setColor(bgColor); 147 | g.fillRect(0, 0, getWidth(), getHeight()); 148 | 149 | float r = Math.max(Math.min(bgColor.getRed() + redStepAmount, 255.0f), 0.0f); 150 | float gr = Math.max(Math.min(bgColor.getGreen() + greenStepAmount, 255.0f), 0.0f); 151 | float b = Math.max(Math.min(bgColor.getBlue() + blueStepAmount, 255.0f), 0.0f); 152 | 153 | bgColor = new Color(r / 255.0f, gr / 255.0f, b / 255.0f); 154 | //steps -= 1.0f; 155 | if (steps <= 0) { 156 | steps = ogSteps; 157 | currentColor = (currentColor + 1) % colors.length; 158 | nextColor = (nextColor + 1) % colors.length; 159 | bgColor = colors[currentColor]; 160 | 161 | int oldRed = colors[currentColor].getRed(); 162 | int newRed = colors[nextColor].getRed(); 163 | int oldBlue = colors[currentColor].getBlue(); 164 | int newBlue = colors[nextColor].getBlue(); 165 | int oldGreen = colors[currentColor].getGreen(); 166 | int newGreen = colors[nextColor].getGreen(); 167 | blueStepAmount = (newBlue - oldBlue) / steps; 168 | redStepAmount = (newRed - oldRed) / steps; 169 | greenStepAmount = (newGreen - oldGreen) / steps; 170 | } 171 | 172 | Window.scene.draw(g2); 173 | } 174 | 175 | public void run() { 176 | double lastFrameTime = 0.0; 177 | try { 178 | while(isRunning) { 179 | double time = Time.getTime(); 180 | double deltaTime = time - lastFrameTime; 181 | lastFrameTime = time; 182 | 183 | update(deltaTime); 184 | } 185 | } catch(Exception e) { 186 | e.printStackTrace(); 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/com/jade/scripts/Button.java: -------------------------------------------------------------------------------- 1 | package com.jade.scripts; 2 | 3 | import com.jade.components.Component; 4 | import com.jade.components.SubSprite; 5 | import com.jade.main.Window; 6 | 7 | import java.awt.*; 8 | import java.awt.event.MouseEvent; 9 | 10 | public abstract class Button extends Component { 11 | 12 | public int width, height; 13 | public SubSprite img, imgSelected; 14 | public String text; 15 | 16 | private boolean isSelected = false; 17 | private int framesForAnimation = 3; 18 | private int framesLeft = 0; 19 | private int wrapLength, fontSize; 20 | private int xBuffer, yBuffer; 21 | 22 | private float debounceTime = 0.1f; 23 | private float debounceTimeLeft = 0.0f; 24 | 25 | public Button(int width, int height, SubSprite img, SubSprite imgSelected, String text) { 26 | this.width = width; 27 | this.height = height; 28 | this.img = img; 29 | this.imgSelected = imgSelected; 30 | this.text = text; 31 | 32 | this.fontSize = 18; 33 | this.wrapLength = 70; 34 | this.xBuffer = (int)( (width - wrapLength) / 2.0); 35 | this.yBuffer = (int)( (height - fontSize) / 2.0); 36 | } 37 | 38 | public abstract void buttonPressed(); 39 | 40 | @Override 41 | public void update(double dt) { 42 | if (debounceTimeLeft > 0) debounceTimeLeft -= dt; 43 | 44 | if (Window.mouseListener.mousePressed && Window.mouseListener.mouseButton == MouseEvent.BUTTON1 && debounceTimeLeft <= 0.0f) { 45 | debounceTimeLeft = debounceTime; 46 | if (Window.mouseListener.x >= this.parent.transform.position.x && Window.mouseListener.x <= this.parent.transform.position.x + width && 47 | Window.mouseListener.y >= this.parent.transform.position.y && Window.mouseListener.y <= this.parent.transform.position.y + height) { 48 | this.buttonPressed(); 49 | isSelected = true; 50 | framesLeft = framesForAnimation; 51 | } 52 | } 53 | 54 | if (isSelected && framesLeft > 0) { 55 | framesLeft--; 56 | } else if (isSelected && framesLeft == 0) { 57 | isSelected = false; 58 | } 59 | } 60 | 61 | @Override 62 | public void draw(Graphics2D g2) { 63 | g2.setColor(Color.WHITE); 64 | g2.setFont(new Font("Pusab", Font.PLAIN, fontSize)); 65 | FontMetrics fm = g2.getFontMetrics(); 66 | 67 | if (!isSelected) { 68 | g2.drawImage(img.subImg.image, (int)this.parent.transform.position.x, (int)this.parent.transform.position.y, width, height, null); 69 | } else { 70 | g2.drawImage(imgSelected.subImg.image, (int)this.parent.transform.position.x, (int)this.parent.transform.position.y, width, height, null); 71 | } 72 | 73 | drawTextWrapped(fm, g2); 74 | } 75 | 76 | private void drawTextWrapped(FontMetrics fm, Graphics2D g2) { 77 | int lineHeight = fm.getHeight(); 78 | 79 | int startX = (int)this.parent.transform.position.x + xBuffer; 80 | int currentX = startX; 81 | int currentY = (int)this.parent.transform.position.y + yBuffer; 82 | 83 | for (char c : text.toCharArray()) { 84 | if (c == '\n') { 85 | currentX = startX; 86 | currentY += lineHeight; 87 | continue; 88 | } 89 | g2.drawString("" + c, currentX, currentY); 90 | 91 | currentX += fm.stringWidth("" + c); 92 | if (currentX >= startX + wrapLength) { 93 | currentX = startX; 94 | currentY += lineHeight; 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/com/jade/scripts/CameraControls.java: -------------------------------------------------------------------------------- 1 | package com.jade.scripts; 2 | 3 | import com.jade.components.Component; 4 | import com.jade.main.Constants; 5 | import com.jade.main.Scene; 6 | import com.jade.main.Window; 7 | 8 | import java.awt.event.MouseEvent; 9 | 10 | public class CameraControls extends Component { 11 | private float prevMx, prevMy; 12 | 13 | @Override 14 | public void start() { 15 | prevMx = 0.0f; 16 | prevMy = 0.0f; 17 | } 18 | 19 | @Override 20 | public CameraControls clone() { 21 | return new CameraControls(); 22 | } 23 | 24 | @Override 25 | public void update(double dt) { 26 | if (Window.mouseListener.mousePressed && Window.mouseListener.mouseButton == MouseEvent.BUTTON2) { 27 | float dx = (Window.mouseListener.x + Window.mouseListener.dx - prevMx); 28 | float dy = (Window.mouseListener.y + Window.mouseListener.dy - prevMy); 29 | Scene.camera.transform.position.x -= dx; 30 | Scene.camera.transform.position.y -= dy; 31 | if (Scene.camera.transform.position.y > Constants.CAMERA_OFFSET_Y + 30) 32 | Scene.camera.transform.position.y = Constants.CAMERA_OFFSET_Y + 30; 33 | } 34 | 35 | prevMx = Window.mouseListener.x + Window.mouseListener.dx; 36 | prevMy = Window.mouseListener.y + Window.mouseListener.dy; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/com/jade/scripts/CursorScript.java: -------------------------------------------------------------------------------- 1 | package com.jade.scripts; 2 | 3 | import com.jade.components.BoxBounds; 4 | import com.jade.components.Component; 5 | import com.jade.components.TriangleBounds; 6 | import com.jade.jade.GameObject; 7 | import com.jade.main.Constants; 8 | import com.jade.main.LevelEditorScene; 9 | import com.jade.main.Window; 10 | 11 | import java.awt.Graphics2D; 12 | import java.awt.Color; 13 | import java.awt.event.KeyEvent; 14 | import java.awt.event.MouseEvent; 15 | import java.util.List; 16 | 17 | public class CursorScript extends Component { 18 | public boolean isEditing = true; 19 | public boolean isPlacing = false; 20 | 21 | private float debounceTime = 0.1f; 22 | private float debounceTimeLeft = 0.0f; 23 | private boolean wasDragged = false; 24 | private float dragX, dragY, dragWidth, dragHeight; 25 | 26 | @Override 27 | public void update(double dt) { 28 | if (debounceTimeLeft > 0.0f) debounceTimeLeft -= dt; 29 | if (isEditing) { 30 | if (Window.mouseListener.mousePressed && Window.mouseListener.mouseButton == MouseEvent.BUTTON1 && debounceTimeLeft <= 0.0f) { 31 | // MOUSE PRESS LEFT BUTTON 32 | if (Window.mouseListener.y > Constants.BUTTON_OFFSET_Y) return; 33 | 34 | debounceTimeLeft = debounceTime; 35 | GameObject selected = LevelEditorScene.getScene().raycastMouseclick((int)Window.mouseListener.x, (int)Window.mouseListener.y); 36 | if (selected != null && Window.keyListener.isKeyPressed(KeyEvent.VK_SHIFT)) { 37 | LevelEditorScene.getScene().addOrRemoveSelectedGameObject(selected); 38 | } else if (selected != null) { 39 | deselectAll(); 40 | selectObj(selected, true); 41 | LevelEditorScene.getScene().addOrRemoveSelectedGameObject(selected); 42 | } else { 43 | deselectAll(); 44 | } 45 | } else if (!Window.mouseListener.mousePressed && wasDragged) { 46 | // DRAG EXIT LEFT 47 | wasDragged = false; 48 | deselectAll(); 49 | List objs = LevelEditorScene.getScene().addBoxSelect(dragX, dragY, dragWidth, dragHeight); 50 | for (GameObject go : objs) { 51 | selectObj(go, true); 52 | } 53 | } else if (Window.keyListener.isKeyPressed(KeyEvent.VK_ESCAPE)) { 54 | deselectAll(); 55 | } 56 | } 57 | } 58 | 59 | @Override 60 | public CursorScript clone() { 61 | return new CursorScript(); 62 | } 63 | 64 | @Override 65 | public void draw(Graphics2D g2) { 66 | if (Window.mouseListener.mouseDragged && Window.mouseListener.mouseButton == MouseEvent.BUTTON1 && !isPlacing) { 67 | wasDragged = true; 68 | g2.setColor(new Color(1, 1, 1, 0.3f)); 69 | dragX = Window.mouseListener.x; 70 | dragY = Window.mouseListener.y; 71 | dragWidth = Window.mouseListener.dx; 72 | dragHeight = Window.mouseListener.dy; 73 | if (dragWidth < 0) { 74 | dragWidth *= -1; 75 | dragX -= dragWidth; 76 | } 77 | if (dragHeight < 0) { 78 | dragHeight *= -1; 79 | dragY -= dragHeight; 80 | } 81 | g2.fillRect((int)dragX, (int)dragY, (int)dragWidth, (int)dragHeight); 82 | } 83 | } 84 | 85 | public void deselectAll() { 86 | for (GameObject go : LevelEditorScene.getScene().getSelectedGameObjects()) { 87 | selectObj(go, false); 88 | } 89 | LevelEditorScene.getScene().getSelectedGameObjects().clear(); 90 | } 91 | 92 | private void selectObj(GameObject obj, boolean val) { 93 | BoxBounds bounds = obj.getComponent(BoxBounds.class); 94 | TriangleBounds tBounds; 95 | if (bounds != null) bounds.isSelected = val; 96 | else if ((tBounds = obj.getComponent(TriangleBounds.class)) != null) tBounds.isSelected = val; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/com/jade/scripts/EditorKeyShortcuts.java: -------------------------------------------------------------------------------- 1 | package com.jade.scripts; 2 | 3 | import com.jade.components.BoxBounds; 4 | import com.jade.components.TriangleBounds; 5 | import com.jade.dataStructures.Vector2; 6 | import com.jade.components.Component; 7 | import com.jade.jade.GameObject; 8 | import com.jade.main.Constants; 9 | import com.jade.main.LevelEditorScene; 10 | import com.jade.main.Window; 11 | 12 | import java.awt.event.KeyEvent; 13 | import java.util.List; 14 | 15 | public class EditorKeyShortcuts extends Component { 16 | 17 | private float debounceTime = 0.1f; 18 | private float debounceTimeLeft = 0.0f; 19 | 20 | @Override 21 | public void update(double dt) { 22 | debounceTimeLeft -= dt; 23 | 24 | if (parent.getComponent(CursorScript.class).isEditing && debounceTimeLeft <= 0.0f) { 25 | if (!Window.keyListener.isKeyPressed(KeyEvent.VK_SHIFT)) { 26 | if (Window.keyListener.isKeyPressed(KeyEvent.VK_UP)) { 27 | moveGameObjects(new Vector2(0.0f, -Constants.TILE_WIDTH)); 28 | } else if (Window.keyListener.isKeyPressed(KeyEvent.VK_DOWN)) { 29 | moveGameObjects(new Vector2(0.0f, Constants.TILE_WIDTH)); 30 | } else if (Window.keyListener.isKeyPressed(KeyEvent.VK_RIGHT)) { 31 | moveGameObjects(new Vector2(Constants.TILE_WIDTH, 0.0f)); 32 | } else if (Window.keyListener.isKeyPressed(KeyEvent.VK_LEFT)) { 33 | moveGameObjects(new Vector2(-Constants.TILE_WIDTH, 0.0f)); 34 | } 35 | } else { 36 | if (Window.keyListener.isKeyPressed(KeyEvent.VK_UP)) { 37 | moveGameObjects(new Vector2(0.0f, -Constants.ONE_TENTH_TILE_WIDTH)); 38 | } else if (Window.keyListener.isKeyPressed(KeyEvent.VK_DOWN)) { 39 | moveGameObjects(new Vector2(0.0f, Constants.ONE_TENTH_TILE_WIDTH)); 40 | } else if (Window.keyListener.isKeyPressed(KeyEvent.VK_RIGHT)) { 41 | moveGameObjects(new Vector2(Constants.ONE_TENTH_TILE_WIDTH, 0.0f)); 42 | } else if (Window.keyListener.isKeyPressed(KeyEvent.VK_LEFT)) { 43 | moveGameObjects(new Vector2(-Constants.ONE_TENTH_TILE_WIDTH, 0.0f)); 44 | } 45 | } 46 | 47 | if (Window.keyListener.isKeyPressed(KeyEvent.VK_DELETE)) { 48 | LevelEditorScene.getScene().deleteSelected(); 49 | } else if (Window.keyListener.isKeyPressed(KeyEvent.VK_CONTROL) && Window.keyListener.isKeyPressed(KeyEvent.VK_D)) { 50 | List dup = LevelEditorScene.getScene().duplicateSelected(); 51 | for (GameObject go : dup) { 52 | if (go.getComponent(BoxBounds.class) != null) go.getComponent(BoxBounds.class).isSelected = true; 53 | else if (go.getComponent(TriangleBounds.class) != null) go.getComponent(TriangleBounds.class).isSelected = true; 54 | } 55 | } else if (Window.keyListener.isKeyPressed(KeyEvent.VK_PAGE_UP)) { 56 | for (GameObject go : LevelEditorScene.getScene().getSelectedGameObjects()) { 57 | LevelEditorScene.getScene().renderingEngine.remove(go); 58 | go.setZIndex(go.zIndex + 1); 59 | LevelEditorScene.getScene().renderingEngine.submit(go); 60 | } 61 | } else if (Window.keyListener.isKeyPressed(KeyEvent.VK_PAGE_DOWN)) { 62 | for (GameObject go : LevelEditorScene.getScene().getSelectedGameObjects()) { 63 | LevelEditorScene.getScene().renderingEngine.remove(go); 64 | go.setZIndex(go.zIndex - 1); 65 | LevelEditorScene.getScene().renderingEngine.submit(go); 66 | } 67 | } 68 | debounceTimeLeft = debounceTime; 69 | } 70 | } 71 | 72 | private void moveGameObjects(Vector2 distance) { 73 | for (GameObject go : LevelEditorScene.getScene().getSelectedGameObjects()) { 74 | go.transform.position = Vector2.add(go.transform.position, distance); 75 | float gridX = (float)(Math.floor(go.transform.position.x / Constants.TILE_WIDTH) * Constants.TILE_WIDTH); 76 | float gridY = (float)(Math.floor(go.transform.position.y / Constants.TILE_WIDTH) * Constants.TILE_WIDTH); 77 | 78 | if (go.transform.position.x < gridX + 3 && go.transform.position.x > gridX - 3) { 79 | go.transform.position.x = gridX; 80 | } 81 | 82 | if (go.transform.position.y < gridY + 3 && go.transform.position.y > gridY - 3) { 83 | go.transform.position.y = gridY; 84 | } 85 | } 86 | } 87 | 88 | @Override 89 | public EditorKeyShortcuts clone() { 90 | return new EditorKeyShortcuts(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/com/jade/scripts/Ground.java: -------------------------------------------------------------------------------- 1 | package com.jade.scripts; 2 | 3 | import com.jade.dataStructures.Vector2; 4 | import com.jade.components.BoxBounds; 5 | import com.jade.components.Component; 6 | import com.jade.jade.GameObject; 7 | import com.jade.main.Constants; 8 | import com.jade.main.LevelScene; 9 | import com.jade.main.Window; 10 | 11 | import java.awt.Graphics2D; 12 | import java.awt.BasicStroke; 13 | import java.awt.Color; 14 | 15 | public class Ground extends Component { 16 | GameObject player; 17 | Vector2 originalPos; 18 | float offsetX; 19 | 20 | public float maxY = 0.0f; 21 | 22 | public Ground(GameObject player) { 23 | this.player = player; 24 | } 25 | 26 | @Override 27 | public void start() { 28 | this.offsetX = Vector2.subtract(player.transform.position, this.parent.transform.position).x; 29 | this.originalPos = this.parent.transform.position; 30 | maxY = this.parent.transform.position.y; 31 | } 32 | 33 | @Override 34 | public Ground clone() { 35 | return new Ground(this.player); 36 | } 37 | 38 | @Override 39 | public void update(double dt) { 40 | this.parent.transform.position = Vector2.subtract(player.transform.position, new Vector2(-Constants.CAMERA_OFFSET_X, 0)); 41 | this.parent.transform.position.y = originalPos.y; 42 | 43 | if (player.transform.position.y > maxY - player.getComponent(BoxBounds.class).height) { 44 | player.transform.position.y = maxY - (float)player.getComponent(BoxBounds.class).height; 45 | player.getComponent(BoxBounds.class).onGround = true; 46 | } 47 | } 48 | 49 | @Override 50 | public void draw(Graphics2D g2) { 51 | g2.setStroke(new BasicStroke(3)); 52 | g2.setColor(Color.WHITE); 53 | g2.drawRect((int)this.parent.transform.position.x, (int)this.parent.transform.position.y, Constants.SCREEN_WIDTH, Constants.SCREEN_HEIGHT); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/com/jade/scripts/Music.java: -------------------------------------------------------------------------------- 1 | package com.jade.scripts; 2 | 3 | import com.jade.components.Component; 4 | 5 | import javax.sound.sampled.AudioInputStream; 6 | import javax.sound.sampled.AudioSystem; 7 | import javax.sound.sampled.Clip; 8 | import java.io.File; 9 | 10 | public class Music extends Component { 11 | 12 | private String filepath; 13 | AudioInputStream audio = null; 14 | Clip audioClip = null; 15 | 16 | public Music(String filename) { 17 | filepath = new File(filename).getAbsolutePath(); 18 | 19 | try { 20 | audio = AudioSystem.getAudioInputStream(new File(filepath).getAbsoluteFile()); 21 | audioClip = AudioSystem.getClip(); 22 | audioClip.open(audio); 23 | restartClip(); 24 | } catch(Exception e) { 25 | e.printStackTrace(); 26 | } 27 | } 28 | 29 | public void stop() { 30 | audioClip.stop(); 31 | } 32 | 33 | public void restartClip() { 34 | try { 35 | audioClip.stop(); 36 | audioClip.setFramePosition(0); 37 | audioClip.start(); 38 | } catch (Exception e) { 39 | e.printStackTrace(); 40 | } 41 | } 42 | 43 | @Override 44 | public Component clone() { 45 | return null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/com/jade/scripts/ParallaxBackground.java: -------------------------------------------------------------------------------- 1 | package com.jade.scripts; 2 | 3 | import com.jade.components.Component; 4 | import com.jade.jade.GameObject; 5 | import com.jade.main.Constants; 6 | import com.jade.main.LevelEditorScene; 7 | import com.jade.main.Scene; 8 | import com.jade.main.Window; 9 | 10 | import javax.imageio.ImageIO; 11 | import java.awt.*; 12 | import java.awt.image.BufferedImage; 13 | import java.io.File; 14 | import java.io.IOException; 15 | 16 | public class ParallaxBackground extends Component { 17 | public int width, height; 18 | public BufferedImage image; 19 | public GameObject[] backgrounds; 20 | public int timeStep = 0; 21 | private float speed = 80.0f; 22 | 23 | private boolean isPlaying; 24 | 25 | private boolean followGround = false; 26 | private Ground ground; 27 | 28 | public ParallaxBackground(int width, int height, String file, GameObject[] backgrounds, boolean isPlaying, Ground ground, boolean followGround) { 29 | this.width = width; 30 | this.height = height; 31 | this.backgrounds = backgrounds; 32 | this.isPlaying = isPlaying; 33 | if (followGround) this.speed = Constants.SPEED - 35; 34 | 35 | try { 36 | this.image = ImageIO.read(new File(file)); 37 | } catch(IOException e) { 38 | e.printStackTrace(); 39 | } 40 | 41 | this.followGround = followGround; 42 | this.ground = ground; 43 | } 44 | 45 | @Override 46 | public void update(double dt) { 47 | if (isPlaying) { 48 | this.timeStep++; 49 | 50 | this.parent.transform.position.x -= dt * speed; 51 | this.parent.transform.position.x = (float) Math.floor(this.parent.transform.position.x); 52 | if (this.parent.transform.position.x < -width) { 53 | float maxX = 0; 54 | int otherTimeStep = 0; 55 | for (GameObject go : backgrounds) { 56 | if (go.transform.position.x > maxX) { 57 | maxX = go.transform.position.x; 58 | otherTimeStep = go.getComponent(ParallaxBackground.class).timeStep; 59 | } 60 | } 61 | 62 | if (otherTimeStep == this.timeStep) { 63 | this.parent.transform.position.x = maxX + width; 64 | } else { 65 | this.parent.transform.position.x = (float) Math.floor((maxX + width) - (dt * speed)); 66 | } 67 | } 68 | } 69 | 70 | if (followGround) { 71 | this.parent.transform.position.y = ground.parent.transform.position.y; 72 | } 73 | } 74 | 75 | @Override 76 | public ParallaxBackground clone() { 77 | return null; 78 | } 79 | 80 | @Override 81 | public void draw(Graphics2D g2) { 82 | if (followGround) { 83 | g2.drawImage(image, (int)this.parent.transform.position.x, (int)(this.parent.transform.position.y - LevelEditorScene.camera.transform.position.y), 84 | width, height, null); 85 | } else { 86 | int height = Math.min((int)(ground.parent.transform.position.y - Scene.camera.transform.position.y), Constants.SCREEN_HEIGHT); 87 | g2.drawImage(image, (int) this.parent.transform.position.x, (int) this.parent.transform.position.y, width, Constants.SCREEN_HEIGHT, null); 88 | g2.setColor(Window.getWindow().groundColor); 89 | g2.fillRect((int)this.parent.transform.position.x, height, width, Constants.SCREEN_HEIGHT - height); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/com/jade/scripts/Player.java: -------------------------------------------------------------------------------- 1 | package com.jade.scripts; 2 | 3 | import com.jade.dataStructures.Vector2; 4 | import com.jade.components.BoxBounds; 5 | import com.jade.components.Component; 6 | import com.jade.jade.GameObject; 7 | import com.jade.main.Constants; 8 | import com.jade.main.LevelScene; 9 | import com.jade.main.Window; 10 | 11 | import java.awt.*; 12 | import java.awt.event.KeyEvent; 13 | import java.awt.image.BufferedImage; 14 | 15 | public class Player extends Component { 16 | private BufferedImage layerOne, layerTwo, outline, spaceship; 17 | public Color layerOneColor, layerTwoColor; 18 | 19 | private boolean isPlaying; 20 | public boolean isJumping = false; 21 | private int debounceFrames = 2; 22 | private int debounceLeft = 0; 23 | public boolean isAi = false; 24 | 25 | // 0 -- Regular 26 | // 1 -- Spaceship 27 | public int state = 0; 28 | 29 | public Player(BufferedImage layerOneImage, BufferedImage layerTwoImage, BufferedImage outline, Color layerOneColor, Color layerTwoColor, BufferedImage spaceship, boolean isPlaying) { 30 | layerOne = layerOneImage; 31 | layerTwo = layerTwoImage; 32 | this.outline = outline; 33 | this.layerOneColor = layerOneColor; 34 | this.layerTwoColor = layerTwoColor; 35 | this.isPlaying = isPlaying; 36 | this.spaceship = spaceship; 37 | } 38 | 39 | @Override 40 | public void start() { 41 | recolor(); 42 | } 43 | 44 | public void recolor() { 45 | int threshold = 200; 46 | for(int y=0; y < layerOne.getWidth(); y++) { 47 | for (int x=0; x < layerOne.getHeight(); x++) { 48 | Color color = new Color(layerOne.getRGB(x, y)); 49 | if (color.getRed() > threshold || color.getGreen() > threshold || color.getBlue() > threshold) { 50 | layerOne.setRGB(x, y, layerOneColor.getRGB()); 51 | } 52 | } 53 | } 54 | 55 | for(int y=0; y < layerTwo.getWidth(); y++) { 56 | for (int x=0; x < layerTwo.getHeight(); x++) { 57 | Color color = new Color(layerTwo.getRGB(x, y)); 58 | if (color.getRed() > threshold || color.getGreen() > threshold || color.getBlue() > threshold) { 59 | layerTwo.setRGB(x, y, layerTwoColor.getRGB()); 60 | } 61 | } 62 | } 63 | } 64 | 65 | @Override 66 | public void update(double dt) { 67 | if (!isPlaying) return; 68 | debounceLeft++; 69 | if (debounceLeft >= debounceFrames) { 70 | debounceLeft = 0; 71 | isJumping = false; 72 | } 73 | 74 | parent.transform.position.x += Constants.SPEED * dt; 75 | 76 | if (state == 0) { 77 | updateRegularState(dt); 78 | } else if (state == 1) { 79 | updateSpaceshipState(dt); 80 | } 81 | 82 | } 83 | 84 | public void jump(BoxBounds bounds) { 85 | if (state == 1) { 86 | addFlyingForce(); 87 | isJumping = true; 88 | } else if (state == 0 && bounds.onGround) { 89 | addJumpForce(); 90 | bounds.onGround = false; 91 | isJumping = true; 92 | } 93 | } 94 | 95 | private void updateSpaceshipState(double dt) { 96 | if (Window.keyListener.isKeyPressed(KeyEvent.VK_SPACE) && !isAi) 97 | jump(parent.getComponent(BoxBounds.class)); 98 | 99 | if (parent.getComponent(BoxBounds.class).onGround) { 100 | parent.getComponent(BoxBounds.class).velocity.y = 0.0f; 101 | } 102 | 103 | if (Math.abs(parent.getComponent(BoxBounds.class).velocity.y) > Constants.FLY_TERMINAL_VELOCITY) { 104 | parent.getComponent(BoxBounds.class).velocity.y = Math.signum(parent.getComponent(BoxBounds.class).velocity.y) * Constants.FLY_TERMINAL_VELOCITY; 105 | } 106 | 107 | Vector2 vel = parent.getComponent(BoxBounds.class).velocity; 108 | parent.getComponent(BoxBounds.class).angle = (vel.y / Constants.TERMINAL_VELOCITY) * 45; 109 | } 110 | 111 | private void updateRegularState(double dt) { 112 | if (Window.keyListener.isKeyPressed(KeyEvent.VK_SPACE) && !isAi) 113 | jump(parent.getComponent(BoxBounds.class)); 114 | 115 | if (!parent.getComponent(BoxBounds.class).onGround) { 116 | parent.getComponent(BoxBounds.class).angle += 300.0f * dt; 117 | } else { 118 | parent.getComponent(BoxBounds.class).angle = (int)Math.floor((parent.getComponent(BoxBounds.class).angle + 5) / 180.0) * 180; 119 | if (parent.getComponent(BoxBounds.class).angle > 180 && parent.getComponent(BoxBounds.class).angle < 360) { 120 | parent.getComponent(BoxBounds.class).angle = 0; 121 | } 122 | isJumping = false; 123 | } 124 | } 125 | 126 | @Override 127 | public Player clone() { 128 | return null; 129 | } 130 | 131 | @Override 132 | public void draw(Graphics2D g2) { 133 | BoxBounds bounds = parent.getComponent(BoxBounds.class); 134 | 135 | if (state == 0) { 136 | Graphics2D oldGraphics = (Graphics2D) g2.create(); 137 | oldGraphics.translate(parent.transform.position.x, parent.transform.position.y); 138 | oldGraphics.rotate(Math.toRadians(bounds.angle), bounds.width / 2.0, bounds.height / 2.0); 139 | oldGraphics.drawImage(layerTwo, 0, 0, Constants.PLR_WIDTH, Constants.PLR_HEIGHT, null); 140 | oldGraphics.drawImage(layerOne, 0, 0, Constants.PLR_WIDTH, Constants.PLR_HEIGHT, null); 141 | oldGraphics.drawImage(outline, 0, 0, Constants.PLR_WIDTH, Constants.PLR_HEIGHT, null); 142 | oldGraphics.dispose(); 143 | } else if (state == 1) { 144 | Graphics2D oldGraphics = (Graphics2D) g2.create(); 145 | oldGraphics.translate(parent.transform.position.x, parent.transform.position.y); 146 | oldGraphics.rotate(Math.toRadians(bounds.angle), bounds.width / 2.0, bounds.height / 2.0); 147 | oldGraphics.drawImage(layerTwo, 12, 12, Constants.PLR_WIDTH / 3, Constants.PLR_HEIGHT / 3, null); 148 | oldGraphics.drawImage(layerOne, 12, 12, Constants.PLR_WIDTH / 3, Constants.PLR_HEIGHT / 3, null); 149 | oldGraphics.drawImage(outline, 12, 12, Constants.PLR_WIDTH / 3, Constants.PLR_HEIGHT / 3, null); 150 | oldGraphics.drawImage(spaceship, 0, 0, Constants.PLR_WIDTH, Constants.PLR_HEIGHT, null); 151 | oldGraphics.dispose(); 152 | } 153 | 154 | } 155 | 156 | public void die() { 157 | if (!isAi) 158 | Window.getWindow().changeScene(1); 159 | else 160 | LevelScene.getScene().resetAi(); 161 | } 162 | 163 | private void addJumpForce() { 164 | parent.getComponent(BoxBounds.class).velocity = new Vector2(0, Constants.JUMP_FORCE); 165 | } 166 | 167 | private void addFlyingForce() { 168 | Vector2 vel = parent.getComponent(BoxBounds.class).velocity; 169 | parent.getComponent(BoxBounds.class).velocity = Vector2.add(vel, new Vector2(0, Constants.FLY_FORCE)); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/com/jade/scripts/Portal.java: -------------------------------------------------------------------------------- 1 | package com.jade.scripts; 2 | 3 | import com.jade.components.BoxBounds; 4 | import com.jade.components.Component; 5 | import com.jade.dataStructures.JString; 6 | import com.jade.file.Parser; 7 | import com.jade.jade.GameObject; 8 | import com.jade.main.LevelScene; 9 | import com.jade.main.Window; 10 | 11 | public class Portal extends Component { 12 | 13 | public float width, height; 14 | public int transformState; 15 | 16 | private boolean hasCollided = false; 17 | 18 | public Portal(float width, float height, int transformState) { 19 | this.width = width; 20 | this.height = height; 21 | this.transformState = transformState; 22 | } 23 | 24 | public Portal clone() {return new Portal(this.width, this.height, this.transformState);} 25 | 26 | @Override 27 | public void update(double dt) { 28 | if (Window.isEditing) return; 29 | if (hasCollided) return; 30 | GameObject plr = LevelScene.getScene().player; 31 | float dx = (plr.transform.position.x + plr.getComponent(BoxBounds.class).halfWidth) - (this.parent.transform.position.x + (width / 2.0f)); 32 | float dy = (plr.transform.position.y + plr.getComponent(BoxBounds.class).halfHeight) - (this.parent.transform.position.y + (height / 2.0f)); 33 | 34 | float combinedHalfWidths = plr.getComponent(BoxBounds.class).halfWidth + (width / 2.0f); 35 | float combinedHalfHeights = plr.getComponent(BoxBounds.class).halfHeight + (height / 2.0f); 36 | 37 | if (Math.abs(dx) < combinedHalfWidths) { 38 | if (Math.abs(dy) < combinedHalfHeights) { 39 | float overlapX = combinedHalfWidths - Math.abs(dx); 40 | float overlapY = combinedHalfHeights - Math.abs(dy); 41 | 42 | if (overlapX >= overlapY) { 43 | if (dy > 0) { 44 | // Collision on the bottom of box 45 | hasCollided = true; 46 | plr.getComponent(Player.class).state = transformState; 47 | } else { 48 | // Collision on the top of box 49 | // Transform 50 | hasCollided = true; 51 | plr.getComponent(Player.class).state = transformState; 52 | } 53 | } else if (Math.abs(dx) < 40) { 54 | // Transform 55 | hasCollided = true; 56 | plr.getComponent(Player.class).state = transformState; 57 | } 58 | } 59 | } 60 | } 61 | 62 | @Override 63 | public String serialize(int tabSize) { 64 | StringBuilder builder = JString.getBuilder(); 65 | 66 | builder.append(beginObjectProperty("Portal", tabSize)); 67 | builder.append(addFloatProperty("width", width, tabSize + 1, true, true)); 68 | builder.append(addFloatProperty("height", height, tabSize + 1, true, true)); 69 | builder.append(addIntProperty("transformState", transformState, tabSize + 1, true, false)); 70 | builder.append(closeObjectProperty(tabSize)); 71 | 72 | return builder.toString(); 73 | } 74 | 75 | public static Portal deserialize() { 76 | Parser.consumeBeginObjectProperty(); 77 | 78 | float width = Parser.consumeFloatProperty("width"); 79 | Parser.consume(','); 80 | float height = Parser.consumeFloatProperty("height"); 81 | Parser.consume(','); 82 | int transformState = Parser.consumeIntProperty("transformState"); 83 | Parser.consume('}'); 84 | 85 | return new Portal(width, height, transformState); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/com/jade/scripts/RotateButton.java: -------------------------------------------------------------------------------- 1 | package com.jade.scripts; 2 | 3 | import com.jade.components.SubSprite; 4 | import com.jade.main.LevelEditorScene; 5 | 6 | public class RotateButton extends Button { 7 | public boolean rotateRight; 8 | 9 | public RotateButton(int width, int height, SubSprite img, SubSprite imgSelected, String text, boolean rotateRight) { 10 | super(width, height, img, imgSelected, text); 11 | this.rotateRight = rotateRight; 12 | } 13 | 14 | @Override 15 | public void buttonPressed() { 16 | if (rotateRight) { 17 | LevelEditorScene.getScene().rotateSelected(90.0); 18 | } else { 19 | LevelEditorScene.getScene().rotateSelected(-90.0); 20 | } 21 | } 22 | 23 | @Override 24 | public RotateButton clone() { 25 | return new RotateButton(width, height, img, imgSelected, text, rotateRight); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/com/jade/scripts/SnapToGrid.java: -------------------------------------------------------------------------------- 1 | package com.jade.scripts; 2 | 3 | import com.jade.components.BoxBounds; 4 | import com.jade.components.SubSprite; 5 | import com.jade.components.TriangleBounds; 6 | import com.jade.dataStructures.Vector2; 7 | import com.jade.jade.GameObject; 8 | import com.jade.jade.Transform; 9 | import com.jade.components.Component; 10 | import com.jade.main.Constants; 11 | import com.jade.main.LevelEditorScene; 12 | import com.jade.main.Window; 13 | 14 | import java.awt.*; 15 | import java.awt.event.KeyEvent; 16 | import java.awt.event.MouseEvent; 17 | 18 | public class SnapToGrid extends Component { 19 | 20 | public int gridWidth, gridHeight; 21 | 22 | private double debounce = 0.0; 23 | private double debounceTime = 0.02; 24 | 25 | public SnapToGrid(int gridWidth, int gridHeight) { 26 | this.gridHeight = gridHeight; 27 | this.gridWidth = gridWidth; 28 | } 29 | 30 | @Override 31 | public void update(double dt) { 32 | debounce -= dt; 33 | 34 | if (this.parent.getComponent(SubSprite.class) != null) { 35 | parent.getComponent(CursorScript.class).deselectAll(); 36 | float x = (float)Math.floor((Window.mouseListener.x + LevelEditorScene.camera.transform.position.x + Window.mouseListener.dx) / gridWidth); 37 | float y = (float)Math.floor((Window.mouseListener.y + LevelEditorScene.camera.transform.position.y + Window.mouseListener.dy) / gridHeight); 38 | this.parent.transform.position.x = x * gridWidth - LevelEditorScene.camera.transform.position.x; 39 | this.parent.transform.position.y = y * gridHeight - LevelEditorScene.camera.transform.position.y; 40 | parent.getComponent(CursorScript.class).isPlacing = true; 41 | 42 | if (Window.mouseListener.mousePressed && debounce < 0 && y * gridHeight < Constants.GROUND_HEIGHT && Window.mouseListener.mouseButton == MouseEvent.BUTTON1) { 43 | debounce = debounceTime; 44 | if (LevelEditorScene.getScene().raycastMouseclick((int)x, (int)y) == null) { 45 | GameObject obj = new GameObject("Generated", new Transform(new Vector2(x * gridWidth, y * gridHeight))); 46 | SubSprite tmp = this.parent.getComponent(SubSprite.class); 47 | obj.addComponent(new SubSprite(tmp.spriteParent.pictureFile, tmp.imgX, tmp.imgY, tmp.width, tmp.height, tmp.row, tmp.column)); 48 | if (parent.getComponent(BoxBounds.class) != null) obj.addComponent(parent.getComponent(BoxBounds.class).clone()); 49 | else if (parent.getComponent(TriangleBounds.class) != null) obj.addComponent(parent.getComponent(TriangleBounds.class).clone()); 50 | if (parent.getComponent(Portal.class) != null) obj.addComponent(parent.getComponent(Portal.class).clone()); 51 | LevelEditorScene.getScene().safeAddGameObject(obj); 52 | } 53 | } 54 | 55 | if (Window.keyListener.isKeyPressed(KeyEvent.VK_ESCAPE)) { 56 | switchToEdit(); 57 | } 58 | } else { 59 | parent.getComponent(CursorScript.class).isPlacing = false; 60 | } 61 | } 62 | 63 | public void switchToEdit() { 64 | this.parent.safeRemoveComponent(SubSprite.class); 65 | this.parent.getComponent(CursorScript.class).isEditing = true; 66 | } 67 | 68 | @Override 69 | public SnapToGrid clone() { 70 | return new SnapToGrid(this.gridWidth, this.gridHeight); 71 | } 72 | 73 | @Override 74 | public void draw(Graphics2D g2) { 75 | SubSprite sprite = parent.getComponent(SubSprite.class); 76 | if (sprite != null && this.parent.transform.position.y + Constants.TILE_WIDTH < Constants.MENU_CONTAINER_Y) { 77 | float alpha = 0.5f; //draw half transparent 78 | AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha); 79 | g2.setComposite(ac); 80 | g2.drawImage(sprite.subImg.image, (int) parent.transform.position.x, (int) parent.transform.position.y, sprite.width, sprite.height, null); 81 | ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f); 82 | g2.setComposite(ac); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/com/jade/ui/Grid.java: -------------------------------------------------------------------------------- 1 | package com.jade.ui; 2 | 3 | import com.jade.components.Component; 4 | import com.jade.main.Constants; 5 | import com.jade.main.LevelEditorScene; 6 | 7 | import java.awt.*; 8 | import java.awt.geom.Line2D; 9 | 10 | public class Grid extends Component { 11 | private int gridSize; 12 | private int numXLines; 13 | private int numYLines; 14 | 15 | private float firstX = 0.0f; 16 | private float firstY = 0.0f; 17 | 18 | private float bottom = 0.0f; 19 | 20 | public Grid(int gridSize) { 21 | this.gridSize = gridSize; 22 | } 23 | 24 | @Override 25 | public void start() { 26 | this.numXLines = Constants.SCREEN_HEIGHT / this.gridSize; 27 | this.numYLines = Constants.SCREEN_WIDTH / this.gridSize; 28 | } 29 | 30 | @Override 31 | public void update(double dt) { 32 | this.firstX = ((float)Math.floor(LevelEditorScene.camera.transform.position.x / gridSize) * gridSize) - LevelEditorScene.camera.transform.position.x; 33 | this.firstY = ((float)Math.floor(LevelEditorScene.camera.transform.position.y / gridSize) * gridSize) - LevelEditorScene.camera.transform.position.y; 34 | this.bottom = Math.min(Constants.GROUND_HEIGHT - LevelEditorScene.camera.transform.position.y, Constants.SCREEN_HEIGHT); 35 | } 36 | 37 | @Override 38 | public Grid clone() { 39 | return new Grid(gridSize); 40 | } 41 | 42 | @Override 43 | public void draw(Graphics2D g2) { 44 | g2.setStroke(new BasicStroke(1f)); 45 | g2.setColor(new Color(0.2f, 0.2f, 0.2f, 0.5f)); 46 | 47 | float x = this.firstX; 48 | float y = this.firstY; 49 | 50 | for (int column=0; column <= numYLines; column++) { 51 | g2.draw(new Line2D.Float(x, 0, x, bottom)); 52 | x += gridSize; 53 | } 54 | 55 | for (int row=0; row <= numXLines; row++) { 56 | if (LevelEditorScene.camera.transform.position.y + y < Constants.GROUND_HEIGHT) { 57 | g2.draw(new Line2D.Float(0, y, Constants.SCREEN_WIDTH, y)); 58 | y += gridSize; 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/com/jade/ui/MainContainer.java: -------------------------------------------------------------------------------- 1 | package com.jade.ui; 2 | 3 | import com.jade.components.*; 4 | import com.jade.dataStructures.Vector2; 5 | import com.jade.jade.GameObject; 6 | import com.jade.jade.Transform; 7 | import com.jade.main.Constants; 8 | import com.jade.main.LevelEditorScene; 9 | import com.jade.scripts.CursorScript; 10 | import com.jade.scripts.Portal; 11 | import com.jade.scripts.RotateButton; 12 | import com.jade.scripts.SnapToGrid; 13 | 14 | import java.awt.Graphics2D; 15 | import java.util.HashMap; 16 | import java.util.List; 17 | import java.util.ArrayList; 18 | import java.util.Map; 19 | 20 | public class MainContainer extends Component { 21 | public List tabs; 22 | public Map> tabObjects; 23 | 24 | private MenuItem currentSelection = null; 25 | private GameObject currentTab = null; 26 | private boolean deselectAll = false; 27 | 28 | public MainContainer() { 29 | this.tabs = new ArrayList<>(); 30 | this.tabObjects = new HashMap<>(); 31 | } 32 | 33 | @Override 34 | public void start() { 35 | Sprite buttonSprites = new Sprite("assets/ui/buttonSprites.png", true, 60, 60, 2, 2, 2); 36 | buttonSprites.loadSpritesheet(); 37 | 38 | Sprite bigButtonSprites = new Sprite("assets/ui/bigButtons.png", true, 90, 90, 4, 2, 4); 39 | bigButtonSprites.loadSpritesheet(); 40 | 41 | Sprite tabSprites = new Sprite("assets/ui/tabs.png", true, Constants.TAB_WIDTH, Constants.TAB_HEIGHT, 6, 2, 6); 42 | tabSprites.loadSpritesheet(); 43 | 44 | LevelEditorScene.cursor.addComponent(new SnapToGrid(42, 42)); 45 | for (int i=0; i < tabSprites.sprites.size(); i++) { 46 | SubSprite currentTab = tabSprites.sprites.get(i); 47 | int x = Constants.TAB_OFFSET_X + (currentTab.column * Constants.TAB_WIDTH) + (currentTab.column * Constants.TAB_HORIZONTAL_SPACING); 48 | int y = Constants.TAB_OFFSET_Y; 49 | GameObject obj = new GameObject("Nonserialize", new Transform(new Vector2(x, y))); 50 | TabItem item = new TabItem(Constants.TAB_WIDTH, Constants.TAB_HEIGHT); 51 | obj.addComponent(currentTab); 52 | obj.addComponent(item); 53 | 54 | this.tabs.add(obj); 55 | this.tabObjects.put(obj, new ArrayList<>()); 56 | LevelEditorScene.getScene().safeAddGameObject(obj); 57 | } 58 | this.currentTab = this.tabs.get(0); 59 | currentTab.getComponent(TabItem.class).setSelected(true); 60 | 61 | addTabObjects(buttonSprites); 62 | addEditingButtons(bigButtonSprites); 63 | } 64 | 65 | @Override 66 | public void update(double dt) { 67 | if (!LevelEditorScene.cursor.getComponent(CursorScript.class).isEditing) { 68 | for (GameObject go : this.tabObjects.get(currentTab)) { 69 | MenuItem item = go.getComponent(MenuItem.class); 70 | if (currentSelection != null && item != currentSelection) { 71 | if (item.isSelected) { 72 | currentSelection.isSelected = false; 73 | currentSelection = item; 74 | break; 75 | } 76 | } else if (currentSelection == null && item.isSelected) { 77 | currentSelection = item; 78 | break; 79 | } 80 | } 81 | deselectAll = true; 82 | } else if (deselectAll) { 83 | for (GameObject go : this.tabObjects.get(currentTab)) { 84 | go.getComponent(MenuItem.class).isSelected = false; 85 | } 86 | deselectAll = false; 87 | } 88 | 89 | for (GameObject go : this.tabs) { 90 | TabItem item = go.getComponent(TabItem.class); 91 | if (item.isSelected && go != currentTab) { 92 | currentTab.getComponent(TabItem.class).setSelected(false); 93 | removeAllGameObjects(tabObjects.get(currentTab)); 94 | currentTab = go; 95 | addGameObjects(tabObjects.get(currentTab)); 96 | break; 97 | } 98 | } 99 | } 100 | 101 | @Override 102 | public void draw(Graphics2D g2) { 103 | for (GameObject go : this.tabs) { 104 | go.getComponent(TabItem.class).draw(g2); 105 | } 106 | 107 | for (GameObject go : this.tabObjects.get(currentTab)) { 108 | go.getComponent(MenuItem.class).draw(g2); 109 | } 110 | } 111 | 112 | @Override 113 | public MainContainer clone() { 114 | return new MainContainer(); 115 | } 116 | 117 | private void removeAllGameObjects(List objs) { 118 | for (GameObject obj : objs) { 119 | LevelEditorScene.getScene().safeRemoveGameObject(obj); 120 | } 121 | } 122 | 123 | private void addGameObjects(List objs) { 124 | for (GameObject obj : objs) { 125 | LevelEditorScene.getScene().safeAddGameObject(obj); 126 | } 127 | } 128 | 129 | private void addEditingButtons(Sprite buttonSprites) { 130 | List buttons = new ArrayList<>(); 131 | 132 | GameObject rotateRight = new GameObject("Nonserialize", new Transform(new Vector2(830, Constants.BUTTON_OFFSET_Y))); 133 | RotateButton rightRotate = new RotateButton(Constants.BIG_BUTTON_WIDTH, Constants.BIG_BUTTON_WIDTH, buttonSprites.sprites.get(2), 134 | buttonSprites.sprites.get(3), "", true); 135 | rotateRight.addComponent(rightRotate); 136 | rotateRight.setUi(true); 137 | rotateRight.setZIndex(100); 138 | buttons.add(rotateRight); 139 | 140 | GameObject rotateLeft = new GameObject("Nonserialize", new Transform(new Vector2(830 + Constants.BIG_BUTTON_WIDTH + 10, Constants.BUTTON_OFFSET_Y))); 141 | RotateButton leftRotate = new RotateButton(Constants.BIG_BUTTON_WIDTH, Constants.BIG_BUTTON_WIDTH, buttonSprites.sprites.get(0), 142 | buttonSprites.sprites.get(1), "", false); 143 | rotateLeft.addComponent(leftRotate); 144 | rotateLeft.setUi(true); 145 | rotateLeft.setZIndex(100); 146 | buttons.add(rotateLeft); 147 | 148 | addGameObjects(buttons); 149 | } 150 | 151 | private void addTabObjects(Sprite buttonSprites) { 152 | Sprite spritesheet = new Sprite("assets/spritesheet.png", true, 42, 42, 6, 2, 12); 153 | spritesheet.loadSpritesheet(); 154 | 155 | Sprite spikeSprites = new Sprite("assets/spikes.png", true, 42, 42, 6, 2, 4); 156 | spikeSprites.loadSpritesheet(); 157 | 158 | Sprite bigSprites = new Sprite("assets/bigSprites.png", true, 84, 84, 2, 2, 2); 159 | bigSprites.loadSpritesheet(); 160 | 161 | Sprite smallBlocks = new Sprite("assets/smallBlocks.png", true, 42, 42, 6, 2, 1); 162 | smallBlocks.loadSpritesheet(); 163 | 164 | Sprite portalSprites = new Sprite("assets/portal.png", true, 44, 85, 2, 2, 2); 165 | portalSprites.loadSpritesheet(); 166 | 167 | // Add tab objects 168 | for (int i=0; i < spritesheet.sprites.size(); i++) { 169 | int x = Constants.BUTTON_OFFSET_X + (spritesheet.sprites.get(i).column * Constants.BUTTON_WIDTH) + (spritesheet.sprites.get(i).column * Constants.BUTTON_HORIZONTAL_SPACING); 170 | int y = Constants.BUTTON_OFFSET_Y + (spritesheet.sprites.get(i).row * Constants.BUTTON_WIDTH) + (spritesheet.sprites.get(i).row * 5); 171 | 172 | // Add first tab 173 | GameObject obj = new GameObject("Nonserialize", new Transform(new Vector2(x, y))); 174 | MenuItem menuItem = new MenuItem(x, y, Constants.BUTTON_WIDTH, Constants.BUTTON_WIDTH, buttonSprites.sprites.get(0), buttonSprites.sprites.get(1)); 175 | obj.addComponent(spritesheet.sprites.get(i)); 176 | obj.addComponent(menuItem); 177 | obj.addComponent(new BoxBounds(Constants.TILE_WIDTH, Constants.TILE_WIDTH, false, false)); 178 | this.tabObjects.get(tabs.get(0)).add(obj); 179 | 180 | // Add second tab 181 | if (i < smallBlocks.sprites.size()) { 182 | obj = new GameObject("Nonserialize", new Transform(new Vector2(x, y))); 183 | menuItem = new MenuItem(x, y, Constants.BUTTON_WIDTH, Constants.BUTTON_WIDTH, buttonSprites.sprites.get(0), buttonSprites.sprites.get(1)); 184 | obj.addComponent(smallBlocks.sprites.get(i)); 185 | obj.addComponent(menuItem); 186 | 187 | if (i == 0) { 188 | obj.addComponent(new BoxBounds(Constants.TILE_WIDTH, 16, 0, false, false, true)); 189 | this.tabObjects.get(tabs.get(1)).add(obj); 190 | } 191 | } 192 | 193 | // Add fourth tab 194 | if (i < spikeSprites.sprites.size()) { 195 | obj = new GameObject("Nonserialize", new Transform(new Vector2(x, y))); 196 | menuItem = new MenuItem(x, y, Constants.BUTTON_WIDTH, Constants.BUTTON_WIDTH, buttonSprites.sprites.get(0), buttonSprites.sprites.get(1)); 197 | menuItem.isBox = false; 198 | obj.addComponent(spikeSprites.sprites.get(i)); 199 | obj.addComponent(menuItem); 200 | 201 | double base = spikeSprites.sprites.get(i).width; 202 | double height = spikeSprites.sprites.get(i).height; 203 | if (i == 1) height = 13; 204 | else if (i == 2) { 205 | base = 24; 206 | height = 24; 207 | } 208 | TriangleBounds tBounds = new TriangleBounds(base, height, false); 209 | 210 | if (i < 3) { 211 | obj.addComponent(tBounds); 212 | } else { 213 | BoxBounds bounds = new BoxBounds(Constants.TILE_WIDTH, 24, false, true); 214 | obj.addComponent(bounds); 215 | menuItem.isBox = true; 216 | } 217 | this.tabObjects.get(tabs.get(3)).add(obj); 218 | } 219 | 220 | // Add fifth tab 221 | if ( i == 0) { 222 | obj = new GameObject("Nonserialize", new Transform(new Vector2(x, y))); 223 | menuItem = new MenuItem(x, y, Constants.BUTTON_WIDTH, Constants.BUTTON_WIDTH, buttonSprites.sprites.get(0), buttonSprites.sprites.get(1)); 224 | obj.addComponent(bigSprites.sprites.get(1)); 225 | obj.addComponent(menuItem); 226 | obj.addComponent(new BoxBounds(Constants.TILE_WIDTH * 2, 56, 0.0, false, false, false)); 227 | this.tabObjects.get(tabs.get(4)).add(obj); 228 | } 229 | 230 | // Add sixth tab 231 | if (i == 0) { 232 | obj = new GameObject("Nonserialize", new Transform(new Vector2(x, y))); 233 | menuItem = new MenuItem(x, y, Constants.BUTTON_WIDTH, Constants.BUTTON_WIDTH, buttonSprites.sprites.get(0), buttonSprites.sprites.get(1)); 234 | obj.addComponent(portalSprites.sprites.get(0)); 235 | obj.addComponent(menuItem);; 236 | obj.addComponent(new BoxBounds(44, 85, 0.0, false, false, false)); 237 | obj.addComponent(new Portal(44, 85, 1)); 238 | this.tabObjects.get(tabs.get(5)).add(obj); 239 | } else if (i == 1) { 240 | obj = new GameObject("Nonserialize", new Transform(new Vector2(x, y))); 241 | menuItem = new MenuItem(x, y, Constants.BUTTON_WIDTH, Constants.BUTTON_WIDTH, buttonSprites.sprites.get(0), buttonSprites.sprites.get(1)); 242 | obj.addComponent(portalSprites.sprites.get(1)); 243 | obj.addComponent(menuItem); 244 | obj.addComponent(new BoxBounds(44, 85, 0.0, false, false, false)); 245 | obj.addComponent(new Portal(44, 85, 0)); 246 | this.tabObjects.get(tabs.get(5)).add(obj); 247 | } 248 | } 249 | addGameObjects(this.tabObjects.get(currentTab)); 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /src/com/jade/ui/MenuItem.java: -------------------------------------------------------------------------------- 1 | package com.jade.ui; 2 | 3 | import com.jade.components.BoxBounds; 4 | import com.jade.components.Component; 5 | import com.jade.components.SubSprite; 6 | import com.jade.components.TriangleBounds; 7 | import com.jade.main.Constants; 8 | import com.jade.main.LevelEditorScene; 9 | import com.jade.main.Window; 10 | import com.jade.scripts.CursorScript; 11 | import com.jade.scripts.Portal; 12 | 13 | import java.awt.*; 14 | import java.awt.event.MouseEvent; 15 | 16 | public class MenuItem extends Component { 17 | int x, y, width, height; 18 | SubSprite button, buttonHover; 19 | 20 | public boolean isSelected = false; 21 | public boolean isBox = true; 22 | 23 | private float buffer = 0.0f; 24 | 25 | public MenuItem(int x, int y, int width, int height, SubSprite button, SubSprite buttonHover) { 26 | this.x = x; 27 | this.y = y; 28 | this.width = width; 29 | this.height = height; 30 | this.button = button; 31 | this.buttonHover = buttonHover; 32 | buffer = (width - Constants.TILE_WIDTH) / 2.0f; 33 | } 34 | 35 | @Override 36 | public void update(double dt) { 37 | if (Window.mouseListener.x > this.x && Window.mouseListener.x < this.x + this.width && Window.mouseListener.y > this.y && Window.mouseListener.y < this.y + this.height) { 38 | if (Window.mouseListener.mousePressed && Window.mouseListener.mouseButton == MouseEvent.BUTTON1) { 39 | LevelEditorScene.cursor.safeRemoveComponent(SubSprite.class); 40 | LevelEditorScene.cursor.safeRemoveComponent(BoxBounds.class); 41 | LevelEditorScene.cursor.safeRemoveComponent(TriangleBounds.class); 42 | SubSprite tmp = this.parent.getComponent(SubSprite.class); 43 | LevelEditorScene.cursor.addComponent(new SubSprite(tmp.spriteParent.pictureFile, tmp.imgX, tmp.imgY, tmp.width, tmp.height, tmp.row, tmp.column)); 44 | 45 | if (isBox) { 46 | BoxBounds bounds = parent.getComponent(BoxBounds.class).clone(); 47 | LevelEditorScene.cursor.addComponent(bounds); 48 | } else { 49 | TriangleBounds tBounds = parent.getComponent(TriangleBounds.class).clone(); 50 | LevelEditorScene.cursor.addComponent(tBounds); 51 | } 52 | 53 | if (parent.getComponent(Portal.class) != null) LevelEditorScene.cursor.addComponent(parent.getComponent(Portal.class).clone()); 54 | 55 | this.isSelected = true; 56 | LevelEditorScene.cursor.getComponent(CursorScript.class).isEditing = false; 57 | } 58 | } 59 | } 60 | 61 | @Override 62 | public MenuItem clone() { 63 | return new MenuItem(this.x, this.y, this.width, this.height, this.button, this.buttonHover); 64 | } 65 | 66 | @Override 67 | public void draw(Graphics2D g2) { 68 | g2.drawImage(this.button.subImg.image, x, y, width, height, null); 69 | g2.drawImage(parent.getComponent(SubSprite.class).subImg.image, (int)(x + buffer), (int)(y + buffer), Constants.TILE_WIDTH, Constants.TILE_WIDTH, null); 70 | if (isSelected) { 71 | g2.drawImage(this.buttonHover.subImg.image, x, y, width, height, null); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/com/jade/ui/TabItem.java: -------------------------------------------------------------------------------- 1 | package com.jade.ui; 2 | 3 | import com.jade.components.Component; 4 | import com.jade.components.SubSprite; 5 | import com.jade.main.LevelEditorScene; 6 | import com.jade.main.Window; 7 | import com.jade.scripts.SnapToGrid; 8 | 9 | import java.awt.Graphics2D; 10 | import java.awt.AlphaComposite; 11 | import java.awt.event.MouseEvent; 12 | 13 | public class TabItem extends Component { 14 | 15 | public int width, height; 16 | public boolean isSelected = false; 17 | 18 | public TabItem(int width, int height) { 19 | this.width = width; 20 | this.height = height; 21 | } 22 | 23 | public void setSelected(boolean val) { 24 | isSelected = val; 25 | } 26 | 27 | @Override 28 | public void update(double dt) { 29 | if (Window.mouseListener.mousePressed && Window.mouseListener.mouseButton == MouseEvent.BUTTON1) { 30 | if (Window.mouseListener.x >= parent.transform.position.x && Window.mouseListener.x <= parent.transform.position.x + width && 31 | Window.mouseListener.y >= parent.transform.position.y && Window.mouseListener.y <= parent.transform.position.y + height) { 32 | isSelected = true; 33 | LevelEditorScene.cursor.getComponent(SnapToGrid.class).switchToEdit(); 34 | } 35 | } 36 | } 37 | 38 | @Override 39 | public TabItem clone() { 40 | return null; 41 | } 42 | 43 | @Override 44 | public void draw(Graphics2D g2) { 45 | float alpha = 0.5f; 46 | if (isSelected) alpha = 1.0f; 47 | AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha); 48 | g2.setComposite(ac); 49 | g2.drawImage(parent.getComponent(SubSprite.class).subImg.image, (int)parent.transform.position.x, (int)parent.transform.position.y, width, height, null); 50 | ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f); 51 | g2.setComposite(ac); 52 | } 53 | } 54 | --------------------------------------------------------------------------------