commit ccbbb0a5b01e2c4903e61d2f4e6d8ca41632fb68 Author: Gnarly Narwhal Date: Sun Mar 10 22:34:10 2019 -0700 Entire project diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8327620 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Archive files +*.jar +*.zip + +# Project files +*.classpath +*.project + +# Other stuff +bin/ +.settings/ +*.bat \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e129676 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +Mit License + +Copyright (c) 2018 Gnarly Narwhal + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..aedf66d --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# Ludum Dare 39 + +## Getting Started + +### Prerequisites +Must have [Java](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) installed +* NOTE: I run Java 8 so I cannot guarantee that it will work on earlier version, but I don't believe I've used anything preventing it from running on some older versions. Click [here](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) to download Java 8. + +Graphics driver must have support for OpenGL 3.3 Core or later + +### What's this all about? +This project is my game for Ludum Dare 39. If you don't know what [Ludum Dare](https://ldjam.com/about) is and you're into making games then definitely check it out [here](https://ldjam.com/about). + +### So what's the game? +Well the game is a game in which you, the player, must control a system of mirrors to direct a laser to the target. You do this by writing programs that control the mirrors and the laser cannons. The game is very short and has some maybe game breaking bugs, but overall its fairly complete. + +### Can I play it? +Of course! Here's how: + +Click [here](https://drive.google.com/open?id=0B30vfj3rcantWWxEcHRZZnRWREE) to download the game. Once you've downloaded it just run the jar file. + +Of course you can always download the source and compile it yourself. You are going to need [LWJGL 3](https://www.lwjgl.org/download). Make sure to include JOML under **Addons** diff --git a/res/img/bars/blcorner.png b/res/img/bars/blcorner.png new file mode 100644 index 0000000..b6836fa Binary files /dev/null and b/res/img/bars/blcorner.png differ diff --git a/res/img/bars/brcorner.png b/res/img/bars/brcorner.png new file mode 100644 index 0000000..a353efb Binary files /dev/null and b/res/img/bars/brcorner.png differ diff --git a/res/img/bars/corner.png b/res/img/bars/corner.png new file mode 100644 index 0000000..3cabf85 Binary files /dev/null and b/res/img/bars/corner.png differ diff --git a/res/img/bars/leftbar.png b/res/img/bars/leftbar.png new file mode 100644 index 0000000..346aff8 Binary files /dev/null and b/res/img/bars/leftbar.png differ diff --git a/res/img/bars/numbers/0.png b/res/img/bars/numbers/0.png new file mode 100644 index 0000000..e6f2440 Binary files /dev/null and b/res/img/bars/numbers/0.png differ diff --git a/res/img/bars/numbers/1.png b/res/img/bars/numbers/1.png new file mode 100644 index 0000000..529ec42 Binary files /dev/null and b/res/img/bars/numbers/1.png differ diff --git a/res/img/bars/numbers/10.png b/res/img/bars/numbers/10.png new file mode 100644 index 0000000..716d690 Binary files /dev/null and b/res/img/bars/numbers/10.png differ diff --git a/res/img/bars/numbers/11.png b/res/img/bars/numbers/11.png new file mode 100644 index 0000000..5757453 Binary files /dev/null and b/res/img/bars/numbers/11.png differ diff --git a/res/img/bars/numbers/12.png b/res/img/bars/numbers/12.png new file mode 100644 index 0000000..2e0c24f Binary files /dev/null and b/res/img/bars/numbers/12.png differ diff --git a/res/img/bars/numbers/2.png b/res/img/bars/numbers/2.png new file mode 100644 index 0000000..a6d956d Binary files /dev/null and b/res/img/bars/numbers/2.png differ diff --git a/res/img/bars/numbers/3.png b/res/img/bars/numbers/3.png new file mode 100644 index 0000000..c3dca18 Binary files /dev/null and b/res/img/bars/numbers/3.png differ diff --git a/res/img/bars/numbers/4.png b/res/img/bars/numbers/4.png new file mode 100644 index 0000000..a0b9934 Binary files /dev/null and b/res/img/bars/numbers/4.png differ diff --git a/res/img/bars/numbers/5.png b/res/img/bars/numbers/5.png new file mode 100644 index 0000000..b6ca49d Binary files /dev/null and b/res/img/bars/numbers/5.png differ diff --git a/res/img/bars/numbers/6.png b/res/img/bars/numbers/6.png new file mode 100644 index 0000000..9fb751d Binary files /dev/null and b/res/img/bars/numbers/6.png differ diff --git a/res/img/bars/numbers/7.png b/res/img/bars/numbers/7.png new file mode 100644 index 0000000..cff9b45 Binary files /dev/null and b/res/img/bars/numbers/7.png differ diff --git a/res/img/bars/numbers/8.png b/res/img/bars/numbers/8.png new file mode 100644 index 0000000..c4be30c Binary files /dev/null and b/res/img/bars/numbers/8.png differ diff --git a/res/img/bars/numbers/9.png b/res/img/bars/numbers/9.png new file mode 100644 index 0000000..be7feda Binary files /dev/null and b/res/img/bars/numbers/9.png differ diff --git a/res/img/bars/tlcorner.png b/res/img/bars/tlcorner.png new file mode 100644 index 0000000..34d50a0 Binary files /dev/null and b/res/img/bars/tlcorner.png differ diff --git a/res/img/bars/topbar.png b/res/img/bars/topbar.png new file mode 100644 index 0000000..a139be1 Binary files /dev/null and b/res/img/bars/topbar.png differ diff --git a/res/img/bars/trcorner.png b/res/img/bars/trcorner.png new file mode 100644 index 0000000..a36e20b Binary files /dev/null and b/res/img/bars/trcorner.png differ diff --git a/res/img/fonts/default.png b/res/img/fonts/default.png new file mode 100644 index 0000000..a3dc711 Binary files /dev/null and b/res/img/fonts/default.png differ diff --git a/res/img/header.png b/res/img/header.png new file mode 100644 index 0000000..f590b34 Binary files /dev/null and b/res/img/header.png differ diff --git a/res/img/map/buttons/menu/hovered.png b/res/img/map/buttons/menu/hovered.png new file mode 100644 index 0000000..eee7225 Binary files /dev/null and b/res/img/map/buttons/menu/hovered.png differ diff --git a/res/img/map/buttons/menu/pressed.png b/res/img/map/buttons/menu/pressed.png new file mode 100644 index 0000000..03ca610 Binary files /dev/null and b/res/img/map/buttons/menu/pressed.png differ diff --git a/res/img/map/buttons/menu/unpressed.png b/res/img/map/buttons/menu/unpressed.png new file mode 100644 index 0000000..c051829 Binary files /dev/null and b/res/img/map/buttons/menu/unpressed.png differ diff --git a/res/img/map/buttons/run/hovered.png b/res/img/map/buttons/run/hovered.png new file mode 100644 index 0000000..b066c4a Binary files /dev/null and b/res/img/map/buttons/run/hovered.png differ diff --git a/res/img/map/buttons/run/pressed.png b/res/img/map/buttons/run/pressed.png new file mode 100644 index 0000000..3912a45 Binary files /dev/null and b/res/img/map/buttons/run/pressed.png differ diff --git a/res/img/map/buttons/run/unpressed.png b/res/img/map/buttons/run/unpressed.png new file mode 100644 index 0000000..f2c5141 Binary files /dev/null and b/res/img/map/buttons/run/unpressed.png differ diff --git a/res/img/map/buttons/smenu/hovered.png b/res/img/map/buttons/smenu/hovered.png new file mode 100644 index 0000000..00f85fa Binary files /dev/null and b/res/img/map/buttons/smenu/hovered.png differ diff --git a/res/img/map/buttons/smenu/pressed.png b/res/img/map/buttons/smenu/pressed.png new file mode 100644 index 0000000..ba6d840 Binary files /dev/null and b/res/img/map/buttons/smenu/pressed.png differ diff --git a/res/img/map/buttons/smenu/unpressed.png b/res/img/map/buttons/smenu/unpressed.png new file mode 100644 index 0000000..c571b5b Binary files /dev/null and b/res/img/map/buttons/smenu/unpressed.png differ diff --git a/res/img/map/buttons/stop/hovered.png b/res/img/map/buttons/stop/hovered.png new file mode 100644 index 0000000..d21c74b Binary files /dev/null and b/res/img/map/buttons/stop/hovered.png differ diff --git a/res/img/map/buttons/stop/pressed.png b/res/img/map/buttons/stop/pressed.png new file mode 100644 index 0000000..72f8984 Binary files /dev/null and b/res/img/map/buttons/stop/pressed.png differ diff --git a/res/img/map/buttons/stop/unpressed.png b/res/img/map/buttons/stop/unpressed.png new file mode 100644 index 0000000..16c5754 Binary files /dev/null and b/res/img/map/buttons/stop/unpressed.png differ diff --git a/res/img/map/cannon.png b/res/img/map/cannon.png new file mode 100644 index 0000000..9c1e93d Binary files /dev/null and b/res/img/map/cannon.png differ diff --git a/res/img/map/empty.png b/res/img/map/empty.png new file mode 100644 index 0000000..2556245 Binary files /dev/null and b/res/img/map/empty.png differ diff --git a/res/img/map/laser/laserBend.png b/res/img/map/laser/laserBend.png new file mode 100644 index 0000000..69d0864 Binary files /dev/null and b/res/img/map/laser/laserBend.png differ diff --git a/res/img/map/laser/laserBendBack.png b/res/img/map/laser/laserBendBack.png new file mode 100644 index 0000000..2ffe467 Binary files /dev/null and b/res/img/map/laser/laserBendBack.png differ diff --git a/res/img/map/laser/laserStraight.png b/res/img/map/laser/laserStraight.png new file mode 100644 index 0000000..1063ade Binary files /dev/null and b/res/img/map/laser/laserStraight.png differ diff --git a/res/img/map/laser/laserStraightBack.png b/res/img/map/laser/laserStraightBack.png new file mode 100644 index 0000000..ca19e15 Binary files /dev/null and b/res/img/map/laser/laserStraightBack.png differ diff --git a/res/img/map/mirror135.png b/res/img/map/mirror135.png new file mode 100644 index 0000000..b443f6b Binary files /dev/null and b/res/img/map/mirror135.png differ diff --git a/res/img/map/mirror45.png b/res/img/map/mirror45.png new file mode 100644 index 0000000..796dfdc Binary files /dev/null and b/res/img/map/mirror45.png differ diff --git a/res/img/map/track.png b/res/img/map/track.png new file mode 100644 index 0000000..7f1cb32 Binary files /dev/null and b/res/img/map/track.png differ diff --git a/res/img/map/trigger.png b/res/img/map/trigger.png new file mode 100644 index 0000000..def072a Binary files /dev/null and b/res/img/map/trigger.png differ diff --git a/res/img/menu/background.png b/res/img/menu/background.png new file mode 100644 index 0000000..5fded32 Binary files /dev/null and b/res/img/menu/background.png differ diff --git a/res/img/menu/close/hovered.png b/res/img/menu/close/hovered.png new file mode 100644 index 0000000..b0e51e6 Binary files /dev/null and b/res/img/menu/close/hovered.png differ diff --git a/res/img/menu/close/pressed.png b/res/img/menu/close/pressed.png new file mode 100644 index 0000000..d8fbd38 Binary files /dev/null and b/res/img/menu/close/pressed.png differ diff --git a/res/img/menu/close/unpressed.png b/res/img/menu/close/unpressed.png new file mode 100644 index 0000000..cd04d5f Binary files /dev/null and b/res/img/menu/close/unpressed.png differ diff --git a/res/img/menu/help/helpMenu.png b/res/img/menu/help/helpMenu.png new file mode 100644 index 0000000..7de272b Binary files /dev/null and b/res/img/menu/help/helpMenu.png differ diff --git a/res/img/menu/help/hovered.png b/res/img/menu/help/hovered.png new file mode 100644 index 0000000..2ba81dc Binary files /dev/null and b/res/img/menu/help/hovered.png differ diff --git a/res/img/menu/help/pressed.png b/res/img/menu/help/pressed.png new file mode 100644 index 0000000..21da877 Binary files /dev/null and b/res/img/menu/help/pressed.png differ diff --git a/res/img/menu/help/unpressed.png b/res/img/menu/help/unpressed.png new file mode 100644 index 0000000..447a6ca Binary files /dev/null and b/res/img/menu/help/unpressed.png differ diff --git a/res/img/menu/next/hovered.png b/res/img/menu/next/hovered.png new file mode 100644 index 0000000..1248ea0 Binary files /dev/null and b/res/img/menu/next/hovered.png differ diff --git a/res/img/menu/next/pressed.png b/res/img/menu/next/pressed.png new file mode 100644 index 0000000..601fa8e Binary files /dev/null and b/res/img/menu/next/pressed.png differ diff --git a/res/img/menu/next/unpressed.png b/res/img/menu/next/unpressed.png new file mode 100644 index 0000000..bb7cbb9 Binary files /dev/null and b/res/img/menu/next/unpressed.png differ diff --git a/res/img/menu/play/hovered.png b/res/img/menu/play/hovered.png new file mode 100644 index 0000000..d3e3f20 Binary files /dev/null and b/res/img/menu/play/hovered.png differ diff --git a/res/img/menu/play/pressed.png b/res/img/menu/play/pressed.png new file mode 100644 index 0000000..4558abb Binary files /dev/null and b/res/img/menu/play/pressed.png differ diff --git a/res/img/menu/play/unpressed.png b/res/img/menu/play/unpressed.png new file mode 100644 index 0000000..e719192 Binary files /dev/null and b/res/img/menu/play/unpressed.png differ diff --git a/res/img/menu/prev/hovered.png b/res/img/menu/prev/hovered.png new file mode 100644 index 0000000..c75728b Binary files /dev/null and b/res/img/menu/prev/hovered.png differ diff --git a/res/img/menu/prev/pressed.png b/res/img/menu/prev/pressed.png new file mode 100644 index 0000000..37a2f17 Binary files /dev/null and b/res/img/menu/prev/pressed.png differ diff --git a/res/img/menu/prev/unpressed.png b/res/img/menu/prev/unpressed.png new file mode 100644 index 0000000..d654f98 Binary files /dev/null and b/res/img/menu/prev/unpressed.png differ diff --git a/res/maps/level1.llm b/res/maps/level1.llm new file mode 100644 index 0000000..87afd60 --- /dev/null +++ b/res/maps/level1.llm @@ -0,0 +1,13 @@ +0 0 0 0 0 4 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 3 0 0 0 0 0 0 + +1 +0 8 \ No newline at end of file diff --git a/res/maps/level2.llm b/res/maps/level2.llm new file mode 100644 index 0000000..b5f250c --- /dev/null +++ b/res/maps/level2.llm @@ -0,0 +1,13 @@ +0 0 0 0 0 0 4 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 1 2 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 3 0 0 0 0 0 0 + +1 +0 10 \ No newline at end of file diff --git a/res/maps/level3.llm b/res/maps/level3.llm new file mode 100644 index 0000000..166b820 --- /dev/null +++ b/res/maps/level3.llm @@ -0,0 +1,13 @@ +0 0 0 0 0 0 4 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 1 2 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 3 0 0 0 0 0 0 + +1 +0 9 \ No newline at end of file diff --git a/res/samby.png b/res/samby.png new file mode 100644 index 0000000..5e77acb Binary files /dev/null and b/res/samby.png differ diff --git a/res/sambyanim.png b/res/sambyanim.png new file mode 100644 index 0000000..07a3753 Binary files /dev/null and b/res/sambyanim.png differ diff --git a/res/shaders/s2a/frag.fs b/res/shaders/s2a/frag.fs new file mode 100644 index 0000000..1bee287 --- /dev/null +++ b/res/shaders/s2a/frag.fs @@ -0,0 +1,13 @@ +#version 330 core + +uniform sampler2D sampler; + +in vec2 texCoords; + +out vec4 color; + +void main() { + color = texture(sampler, texCoords); + if(color.a == 0) + discard; +} \ No newline at end of file diff --git a/res/shaders/s2a/vert.vs b/res/shaders/s2a/vert.vs new file mode 100644 index 0000000..025755b --- /dev/null +++ b/res/shaders/s2a/vert.vs @@ -0,0 +1,17 @@ +#version 330 core + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; + +uniform vec2 animProps; // Loc 0 - Frame Width, Loc 1 - Offset + +layout (location = 0) in vec3 vertices; +layout (location = 1) in vec2 iTexCoords; + +out vec2 texCoords; + +void main() { + texCoords = vec2(((iTexCoords.x * animProps.x) + animProps.y), iTexCoords.y); + gl_Position = projection * view * model * vec4(vertices, 1.0); +} diff --git a/res/shaders/s2c/frag.fs b/res/shaders/s2c/frag.fs new file mode 100644 index 0000000..0027877 --- /dev/null +++ b/res/shaders/s2c/frag.fs @@ -0,0 +1,9 @@ +#version 330 core + +uniform vec4 iColor; + +out vec4 color; + +void main() { + color = iColor; +} \ No newline at end of file diff --git a/res/shaders/s2c/vert.vs b/res/shaders/s2c/vert.vs new file mode 100644 index 0000000..15cc1d2 --- /dev/null +++ b/res/shaders/s2c/vert.vs @@ -0,0 +1,11 @@ +#version 330 core + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; + +layout (location = 0) in vec3 vertices; + +void main() { + gl_Position = projection * view * model * vec4(vertices, 1.0); +} \ No newline at end of file diff --git a/res/shaders/s2t/frag.fs b/res/shaders/s2t/frag.fs new file mode 100644 index 0000000..eba5ec8 --- /dev/null +++ b/res/shaders/s2t/frag.fs @@ -0,0 +1,13 @@ +#version 330 core + +uniform sampler2D sampler; + +in vec2 texCoords; + +layout (location = 0) out vec4 color; + +void main() { + color = texture(sampler, texCoords); + if(color.a == 0) + discard; +} \ No newline at end of file diff --git a/res/shaders/s2t/vert.vs b/res/shaders/s2t/vert.vs new file mode 100644 index 0000000..500c452 --- /dev/null +++ b/res/shaders/s2t/vert.vs @@ -0,0 +1,15 @@ +#version 330 core + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; + +layout (location = 0) in vec3 vertices; +layout (location = 1) in vec2 iTexCoords; + +out vec2 texCoords; + +void main() { + texCoords = iTexCoords; + gl_Position = projection * view * model * vec4(vertices, 1.0); +} \ No newline at end of file diff --git a/res/shaders/s2x/frag.fs b/res/shaders/s2x/frag.fs new file mode 100644 index 0000000..ab4800c --- /dev/null +++ b/res/shaders/s2x/frag.fs @@ -0,0 +1,15 @@ +#version 330 core + +uniform vec4 iColor; + +uniform sampler2D sampler; + +in vec2 texCoords; + +out vec4 color; + +void main() { + color = iColor * texture(sampler, texCoords); + if(color.a == 0) + discard; +} \ No newline at end of file diff --git a/res/shaders/s2x/vert.vs b/res/shaders/s2x/vert.vs new file mode 100644 index 0000000..123624a --- /dev/null +++ b/res/shaders/s2x/vert.vs @@ -0,0 +1,17 @@ +#version 330 core + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; + +uniform vec2 character; + +layout (location = 0) in vec3 vertices; +layout (location = 1) in vec2 iTexCoords; + +out vec2 texCoords; + +void main() { + texCoords = iTexCoords + character; + gl_Position = projection * view * model * vec4(vertices, 1.0); +} \ No newline at end of file diff --git a/src/com/gnarly/engine/display/Camera.java b/src/com/gnarly/engine/display/Camera.java new file mode 100644 index 0000000..eddcfc5 --- /dev/null +++ b/src/com/gnarly/engine/display/Camera.java @@ -0,0 +1,65 @@ +package com.gnarly.engine.display; + +import org.joml.Matrix4f; +import org.joml.Vector3f; + +public class Camera { + + private float width, height; + private Matrix4f projection; + private Vector3f position; + private float rotation; + + public Camera(int width, int height) { + position = new Vector3f(); + rotation = 0; + projection = new Matrix4f(); + setDims(width, height); + } + + public void setDims(float width, float height) { + this.width = width; + this.height = height; + projection.setOrtho(0, width, height, 0, 1, -1); + } + + public void translate(int x, int y) { + position.sub(x, y, 0); + } + + public void translate(Vector3f vector) { + position.sub(vector); + } + + public void setPosition(int x, int y) { + position.set(x, y, 0); + } + + public void setPosition(Vector3f vector) { + position.set(vector); + } + + public void rotate(float angle) { + this.rotation -= angle; + } + + public void setRotation(float angle) { + this.rotation = angle; + } + + public float getWidth() { + return width; + } + + public float getHeight() { + return height; + } + + public Matrix4f getProjection() { + return projection; + } + + public Matrix4f getView() { + return new Matrix4f().rotateZ(rotation).translate(position); + } +} diff --git a/src/com/gnarly/engine/display/Framebuffer.java b/src/com/gnarly/engine/display/Framebuffer.java new file mode 100644 index 0000000..487fa93 --- /dev/null +++ b/src/com/gnarly/engine/display/Framebuffer.java @@ -0,0 +1,65 @@ +package com.gnarly.engine.display; + +import java.nio.ByteBuffer; + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL30.*; +import static org.lwjgl.opengl.GL32.*; + +public class Framebuffer { + + private int fbo; + private int texture; + private int depth; + + private int width, height; + + public Framebuffer(int width, int height) { + this.width = width; + this.height = height; + fbo = createFrameBuffer(); + texture = createTextureAttachment(); + depth = createDepthBufferAttachment(); + } + + public void cleanUp() { + glDeleteFramebuffers(fbo); + glDeleteTextures(texture); + glDeleteRenderbuffers(depth); + } + + public void bind() { + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glViewport(0, 0, width, height); + } + + public int getTexture() { + return texture; + } + + private int createFrameBuffer() { + int frameBuffer = glGenFramebuffers(); + glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer); + glDrawBuffer(GL_COLOR_ATTACHMENT0); + return frameBuffer; + } + + private int createTextureAttachment() { + int texture = glGenTextures(); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, (ByteBuffer) null); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0); + return texture; + } + + private int createDepthBufferAttachment() { + int depthBuffer = glGenRenderbuffers(); + glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer); + return depthBuffer; + } +} \ No newline at end of file diff --git a/src/com/gnarly/engine/display/Window.java b/src/com/gnarly/engine/display/Window.java new file mode 100644 index 0000000..6ad56c2 --- /dev/null +++ b/src/com/gnarly/engine/display/Window.java @@ -0,0 +1,279 @@ +package com.gnarly.engine.display; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.glfw.GLFW.GLFW_CONTEXT_VERSION_MINOR; +import static org.lwjgl.glfw.GLFW.GLFW_FALSE; +import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_CORE_PROFILE; +import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_PROFILE; +import static org.lwjgl.glfw.GLFW.GLFW_PRESS; +import static org.lwjgl.glfw.GLFW.GLFW_RESIZABLE; +import static org.lwjgl.glfw.GLFW.GLFW_TRUE; +import static org.lwjgl.glfw.GLFW.glfwCreateWindow; +import static org.lwjgl.glfw.GLFW.glfwGetCursorPos; +import static org.lwjgl.glfw.GLFW.glfwGetKey; +import static org.lwjgl.glfw.GLFW.glfwGetMouseButton; +import static org.lwjgl.glfw.GLFW.glfwGetPrimaryMonitor; +import static org.lwjgl.glfw.GLFW.glfwGetVideoMode; +import static org.lwjgl.glfw.GLFW.glfwInit; +import static org.lwjgl.glfw.GLFW.glfwMakeContextCurrent; +import static org.lwjgl.glfw.GLFW.glfwPollEvents; +import static org.lwjgl.glfw.GLFW.glfwSetErrorCallback; +import static org.lwjgl.glfw.GLFW.glfwSetScrollCallback; +import static org.lwjgl.glfw.GLFW.glfwSetWindowPos; +import static org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose; +import static org.lwjgl.glfw.GLFW.glfwSetWindowSizeCallback; +import static org.lwjgl.glfw.GLFW.glfwSwapBuffers; +import static org.lwjgl.glfw.GLFW.glfwSwapInterval; +import static org.lwjgl.glfw.GLFW.glfwWindowHint; +import static org.lwjgl.glfw.GLFW.glfwWindowShouldClose; +import static org.lwjgl.opengl.GL.createCapabilities; +import static org.lwjgl.opengl.GL11.GL_BLEND; +import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; +import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; +import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST; +import static org.lwjgl.opengl.GL11.GL_ONE_MINUS_SRC_ALPHA; +import static org.lwjgl.opengl.GL11.GL_SRC_ALPHA; +import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D; +import static org.lwjgl.opengl.GL11.glBlendFunc; +import static org.lwjgl.opengl.GL11.glClear; +import static org.lwjgl.opengl.GL11.glClearColor; +import static org.lwjgl.opengl.GL11.glEnable; +import static org.lwjgl.opengl.GL11.glViewport; +import static org.lwjgl.opengl.GL30.*; +import static org.lwjgl.system.MemoryUtil.NULL; + +import org.joml.Vector3f; +import org.lwjgl.glfw.GLFWCharCallback; +import org.lwjgl.glfw.GLFWCharModsCallback; +import org.lwjgl.glfw.GLFWErrorCallback; +import org.lwjgl.glfw.GLFWKeyCallback; +import org.lwjgl.glfw.GLFWScrollCallback; +import org.lwjgl.glfw.GLFWVidMode; +import org.lwjgl.glfw.GLFWWindowSizeCallback; + +public class Window { + + public static final byte + UNPRESSED = 0, + RELEASED = 1, + PRESSED = 2, + HELD = 3; + + private long window; + + private GLFWVidMode vidMode; + + private int xOff, yOff, vWidth, vHeight; + + private double scrollX, scrollY; + + private StringBuilder input; + + private boolean resized; + + byte[] keys = new byte[GLFW_KEY_LAST]; + + public Window(int width, int height, boolean vSync, boolean resizable, String title) { + init(width, height, vSync, resizable, title); + } + + public Window(boolean vSync) { + init(0, 0, vSync, false, null); + } + + private void init(int width, int height, boolean vSync, boolean resizable, String title) { + glfwSetErrorCallback(GLFWErrorCallback.createPrint(System.err)); + + if(!glfwInit()) { + System.err.println("GLFW failed to initialize!"); + System.exit(-1); + } + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + glfwWindowHint(GLFW_RESIZABLE, resizable ? GLFW_TRUE : GLFW_FALSE); + + vidMode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + if(width == 0 || height == 0 || width >= vidMode.width() || height >= vidMode.height()) { + width = vidMode.width(); + height = vidMode.height(); + window = glfwCreateWindow(width, height, "", glfwGetPrimaryMonitor(), NULL); + } + else { + window = glfwCreateWindow(width, height, title, NULL, NULL); + glfwSetWindowPos(window, (vidMode.width() - width) / 2, (vidMode.height() - height) / 2); + } + + glfwMakeContextCurrent(window); + createCapabilities(); + + glfwSwapInterval(vSync ? 1 : 0); + + glfwSetWindowSizeCallback(window, new GLFWWindowSizeCallback() { + public void invoke(long window, int width, int height) { + resized = true; + glViewport(0, 0, width, height); + } + }); + + glfwSetScrollCallback(window, new GLFWScrollCallback() { + public void invoke(long window, double x, double y) { + scrollX = x; + scrollY = y; + } + }); + + input = new StringBuilder(); + glfwSetCharModsCallback(window, new GLFWCharModsCallback() { + public void invoke(long window, int key, int mods) { + input.append((char) key); + } + }); + glfwSetKeyCallback(window, new GLFWKeyCallback() { + public void invoke(long window, int key, int scancode, int action, int mods) { + if(action != GLFW_RELEASE) { + if(glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) != GLFW_PRESS && glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) != GLFW_PRESS && glfwGetKey(window, GLFW_KEY_LEFT_ALT) != GLFW_PRESS && glfwGetKey(window, GLFW_KEY_RIGHT_ALT) != GLFW_PRESS) { + switch (key) { + case GLFW_KEY_ENTER: + input.append((char) 10); + break; + case GLFW_KEY_BACKSPACE: + input.append((char) 8); + break; + case GLFW_KEY_DELETE: + input.append((char) 127); + break; + case GLFW_KEY_LEFT: + input.append((char) 1); + break; + case GLFW_KEY_RIGHT: + input.append((char) 2); + break; + case GLFW_KEY_UP: + input.append((char) 3); + break; + case GLFW_KEY_DOWN: + input.append((char) 4); + break; + case GLFW_KEY_TAB: + input.append((char) 9); + break; + } + } + if(keys[key] < PRESSED) + keys[key] = PRESSED; + else + keys[key] = HELD; + } + else { + if(keys[key] > RELEASED) + keys[key] = RELEASED; + else + keys[key] = UNPRESSED; + } + } + }); + + float vWidth = 0, vHeight = 0; + while(vWidth < vidMode.width() && vHeight < vidMode.height()) { + vWidth += 1; + vHeight += 9 /16f; + } + + xOff = (int) ((vidMode.width() - vWidth) / 2); + yOff = (int) ((vidMode.height() - vHeight) / 2); + this.vWidth = (int) vWidth; + this.vHeight = (int) vHeight; + bind(); + + glClearColor(0, 0, 0, 1); + + glEnable(GL_TEXTURE_2D); + + glEnable(GL_DEPTH_TEST); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + public void update() { + for (int i = 0; i < keys.length; i++) { + if(keys[i] == PRESSED) + ++keys[i]; + else if(keys[i] == RELEASED) + --keys[i]; + } + scrollX = 0; + scrollY = 0; + resized = false; + input.delete(0, input.length()); + glfwPollEvents(); + } + + public void clear() { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + + public void swap() { + glfwSwapBuffers(window); + } + + public void setVSync(boolean vSync) { + glfwSwapInterval(vSync ? 1 : 0); + } + + public void close() { + glfwSetWindowShouldClose(window, true); + } + + public boolean shouldClose() { + return glfwWindowShouldClose(window); + } + + public String getInput() { + return input.toString(); + } + + public byte getKey(int keyCode) { + return keys[keyCode]; + } + + public Vector3f getMouseCoords(Camera camera) { + double[] x = new double[1], y = new double[1]; + glfwGetCursorPos(window, x, y); + Vector3f ret = new Vector3f((float) x[0] - xOff, (float) y[0] - yOff, 0); + ret.mul(camera.getWidth() / vWidth, camera.getHeight() / vHeight, 1); + return ret; + } + + public boolean mousePressed(int button) { + return glfwGetMouseButton(window, button) == GLFW_PRESS; + } + + public int getWidth() { + return vWidth; + } + + public int getHeight() { + return vHeight; + } + + public boolean wasResized() { + return resized; + } + + public float getScrollX() { + return (float) scrollX; + } + + public float getScrollY() { + return (float) scrollY; + } + + public void bind() { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(xOff, yOff, vWidth, vHeight); + } +} diff --git a/src/com/gnarly/engine/model/Vao.java b/src/com/gnarly/engine/model/Vao.java new file mode 100644 index 0000000..d86f12d --- /dev/null +++ b/src/com/gnarly/engine/model/Vao.java @@ -0,0 +1,68 @@ +package com.gnarly.engine.model; + +import static org.lwjgl.opengl.GL11.GL_FLOAT; +import static org.lwjgl.opengl.GL11.GL_TRIANGLES; +import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT; +import static org.lwjgl.opengl.GL11.glDrawElements; +import static org.lwjgl.opengl.GL15.GL_ARRAY_BUFFER; +import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER; +import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW; +import static org.lwjgl.opengl.GL15.glBindBuffer; +import static org.lwjgl.opengl.GL15.glBufferData; +import static org.lwjgl.opengl.GL15.glDeleteBuffers; +import static org.lwjgl.opengl.GL15.glGenBuffers; +import static org.lwjgl.opengl.GL20.glDisableVertexAttribArray; +import static org.lwjgl.opengl.GL20.glEnableVertexAttribArray; +import static org.lwjgl.opengl.GL20.glVertexAttribPointer; +import static org.lwjgl.opengl.GL30.glBindVertexArray; +import static org.lwjgl.opengl.GL30.glDeleteVertexArrays; +import static org.lwjgl.opengl.GL30.glGenVertexArrays; + +import java.util.ArrayList; + +public class Vao { + + private int numAttribs = 0; + + private int vao, ibo, count; + + private ArrayList vbos = new ArrayList<>(); + + public Vao(float[] vertices, int[] indices) { + vao = glGenVertexArrays(); + glBindVertexArray(vao); + addAttribute(vertices, 3); + ibo = glGenBuffers(); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW); + count = indices.length; + } + + public void addAttribute(float[] data, int size) { + int vbo = glGenBuffers(); + vbos.add(vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, data, GL_STATIC_DRAW); + glVertexAttribPointer(numAttribs, size, GL_FLOAT, false, 0, 0); + ++numAttribs; + } + + public void render() { + glBindVertexArray(vao); + + for (int i = 0; i < numAttribs; ++i) + glEnableVertexAttribArray(i); + + glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, 0); + + for (int i = 0; i < numAttribs; ++i) + glDisableVertexAttribArray(i); + } + + public void destroy() { + for(int vbo : vbos) + glDeleteBuffers(vbo); + glDeleteBuffers(ibo); + glDeleteVertexArrays(vao); + } +} diff --git a/src/com/gnarly/engine/rects/ColRect.java b/src/com/gnarly/engine/rects/ColRect.java new file mode 100644 index 0000000..1c9f961 --- /dev/null +++ b/src/com/gnarly/engine/rects/ColRect.java @@ -0,0 +1,42 @@ +package com.gnarly.engine.rects; + +import org.joml.Matrix4f; +import org.joml.Vector3f; + +import com.gnarly.engine.display.Camera; +import com.gnarly.engine.shaders.Shader; +import com.gnarly.engine.shaders.Shader2c; + +public class ColRect extends Rect { + + private Shader2c shader; + + private float r, g, b, a; + + public ColRect(Camera camera, float x, float y, float depth, float width, float height, float r, float g, float b, float a) { + this.camera = camera; + if(vao == null) + initVao(); + position = new Vector3f(x, y, depth); + rotation = 0; + sx = width / dims.x; + sy = height / dims.y; + flipX = 1; + flipY = 1; + shader = Shader.SHADER2C; + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } + + public void render() { + shader.enable(); + shader.setProjection(camera.getProjection()); + shader.setView(camera.getView()); + shader.setModel(new Matrix4f().translate(position).translate(dims.mul(0.5f * sx, 0.5f * sy, 1, new Vector3f())).rotateZ(rotation).scale(sx * flipX, sy * flipY, 1).translate(dims.mul(0.5f, new Vector3f()).negate())); + shader.setColor(r, g, b, a); + vao.render(); + shader.disable(); + } +} diff --git a/src/com/gnarly/engine/rects/Rect.java b/src/com/gnarly/engine/rects/Rect.java new file mode 100644 index 0000000..c578d74 --- /dev/null +++ b/src/com/gnarly/engine/rects/Rect.java @@ -0,0 +1,84 @@ +package com.gnarly.engine.rects; + +import org.joml.Vector3f; + +import com.gnarly.engine.display.Camera; +import com.gnarly.engine.model.Vao; + +public abstract class Rect { + + protected static final Vector3f dims = new Vector3f(10, 10, 0); + protected static Vao vao = null; + + protected Camera camera; + + protected Vector3f position; + protected float rotation; + protected float sx, sy; + protected byte flipX, flipY; + + protected void initVao() { + float[] vertices = new float[] { + 0, 0, 0.0f, + 0, dims.y, 0.0f, + dims.x, dims.y, 0.0f, + dims.x, 0, 0.0f + }; + int[] indices = new int[] { + 0, 1, 3, + 3, 1, 2 + }; + float[] texCoords = new float[] { + 0, 0, + 0, 1, + 1, 1, + 1, 0 + }; + vao = new Vao(vertices, indices); + vao.addAttribute(texCoords, 2); + } + + public abstract void render(); + + public float getX() { + return position.x; + } + + public float getY() { + return position.y; + } + + public float getWidth() { + return sx * dims.x; + } + + public float getHeight() { + return sy * dims.y; + } + + public void setRotation(float angle) { + this.rotation = (angle * 3.1415926535f) / 180f; + } + + public void setPos(float x, float y) { + this.position.x = x; + this.position.y = y; + } + + public void setSize(float width, float height) { + this.sx = width / dims.x; + this.sy = height / dims.y; + } + + public void setFlip(int x, int y) { + this.flipX = (byte) x; + this.flipY = (byte) y; + } + + public void reset(float x, float y, float width, float height) { + this.position.x = x; + this.position.y = y; + this.sx = width / dims.x; + this.sy = height / dims.y; + } +} diff --git a/src/com/gnarly/engine/rects/TexRect.java b/src/com/gnarly/engine/rects/TexRect.java new file mode 100644 index 0000000..5d9ba50 --- /dev/null +++ b/src/com/gnarly/engine/rects/TexRect.java @@ -0,0 +1,94 @@ +package com.gnarly.engine.rects; + +import org.joml.Matrix4f; +import org.joml.Vector3f; + +import com.gnarly.engine.display.Camera; +import com.gnarly.engine.shaders.Shader; +import com.gnarly.engine.shaders.Shader2a; +import com.gnarly.engine.texture.Anim; +import com.gnarly.engine.texture.Texture; + +public class TexRect extends Rect { + + private Camera camera; + private Texture texture; + private Shader shader; + + public TexRect(Camera camera, String texPath, float x, float y, float depth, float width, float height) { + this.camera = camera; + if(vao == null) + initVao(); + position = new Vector3f(x, y, depth); + rotation = 0; + sx = width / dims.x; + sy = height / dims.y; + flipX = 1; + flipY = 1; + texture = new Texture(texPath); + shader = Shader.SHADER2T; + } + + public TexRect(Camera camera, String texPath, int frames, int fps, float x, float y, float depth, float width, float height) { + this.camera = camera; + if(vao == null) + initVao(); + position = new Vector3f(x, y, depth); + rotation = 0; + sx = width / dims.x; + sy = height / dims.y; + flipX = 1; + flipY = 1; + texture = new Anim(texPath, frames, fps); + shader = Shader.SHADER2A; + } + + public TexRect(Camera camera, Texture texture, float x, float y, float depth, float width, float height) { + this.camera = camera; + if(vao == null) + initVao(); + position = new Vector3f(x, y, depth); + rotation = 0; + sx = width / dims.x; + sy = height / dims.y; + flipX = 1; + flipY = 1; + this.texture = texture; + shader = Shader.SHADER2T; + } + + public TexRect(Camera camera, Anim anim, float x, float y, float depth, float width, float height) { + this.camera = camera; + if(vao == null) + initVao(); + position = new Vector3f(x, y, depth); + rotation = 0; + sx = width / dims.x; + sy = height / dims.y; + flipX = 1; + flipY = 1; + texture = anim; + shader = Shader.SHADER2A; + } + + public void render() { + texture.bind(); + shader.enable(); + shader.setProjection(camera.getProjection()); + shader.setView(camera.getView()); + shader.setModel(new Matrix4f().translate(position).translate(dims.mul(0.5f * sx, 0.5f * sy, 1, new Vector3f())).rotateZ(rotation).scale(sx * flipX, sy * flipY, 1).translate(dims.mul(0.5f, new Vector3f()).negate())); + if(shader instanceof Shader2a) + ((Shader2a) shader).setAnim((Anim) texture); + vao.render(); + shader.disable(); + texture.unbind(); + } + + public void pause() { + ((Anim) texture).pause(); + } + + public void setFrame(int frame) { + ((Anim) texture).setFrame(frame); + } +} diff --git a/src/com/gnarly/engine/shaders/Shader.java b/src/com/gnarly/engine/shaders/Shader.java new file mode 100644 index 0000000..5b7ef71 --- /dev/null +++ b/src/com/gnarly/engine/shaders/Shader.java @@ -0,0 +1,131 @@ +package com.gnarly.engine.shaders; + +import static org.lwjgl.opengl.GL20.GL_COMPILE_STATUS; +import static org.lwjgl.opengl.GL20.GL_FRAGMENT_SHADER; +import static org.lwjgl.opengl.GL20.GL_VERTEX_SHADER; +import static org.lwjgl.opengl.GL20.glAttachShader; +import static org.lwjgl.opengl.GL20.glCompileShader; +import static org.lwjgl.opengl.GL20.glCreateProgram; +import static org.lwjgl.opengl.GL20.glCreateShader; +import static org.lwjgl.opengl.GL20.glDeleteShader; +import static org.lwjgl.opengl.GL20.glDetachShader; +import static org.lwjgl.opengl.GL20.glGetShaderInfoLog; +import static org.lwjgl.opengl.GL20.glGetShaderi; +import static org.lwjgl.opengl.GL20.glGetUniformLocation; +import static org.lwjgl.opengl.GL20.glLinkProgram; +import static org.lwjgl.opengl.GL20.glShaderSource; +import static org.lwjgl.opengl.GL20.glUniformMatrix4fv; +import static org.lwjgl.opengl.GL20.glUseProgram; +import static org.lwjgl.opengl.GL20.glValidateProgram; +import static org.lwjgl.opengl.GL32.GL_GEOMETRY_SHADER; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + +import org.joml.Matrix4f; + +public abstract class Shader { + + public static final Shader2a SHADER2A = new Shader2a(); + public static final Shader2c SHADER2C = new Shader2c(); + public static final Shader2t SHADER2T = new Shader2t(); + public static final Shader2x SHADER2X = new Shader2x(); + + protected int program; + + protected int projLoc, viewLoc, modelLoc; + + protected Shader(String vertPath, String fragPath) { + program = glCreateProgram(); + + int vert = load(vertPath, GL_VERTEX_SHADER); + int frag = load(fragPath, GL_FRAGMENT_SHADER); + + glAttachShader(program, vert); + glAttachShader(program, frag); + + glLinkProgram(program); + glValidateProgram(program); + + glDetachShader(program, vert); + glDetachShader(program, frag); + + glDeleteShader(vert); + glDeleteShader(frag); + + getUniformLocs(); + } + + protected Shader(String vertPath, String fragPath, String geomPath) { + program = glCreateProgram(); + + int vert = load(vertPath, GL_VERTEX_SHADER); + int frag = load(fragPath, GL_FRAGMENT_SHADER); + int geom = load(geomPath, GL_GEOMETRY_SHADER); + + glAttachShader(program, vert); + glAttachShader(program, frag); + glAttachShader(program, geom); + + glLinkProgram(program); + glValidateProgram(program); + + glDetachShader(program, vert); + glDetachShader(program, frag); + glDetachShader(program, geom); + + glDeleteShader(vert); + glDeleteShader(frag); + glDeleteShader(geom); + + getUniformLocs(); + } + + private int load(String path, int type) { + StringBuilder file = new StringBuilder(); + try { + BufferedReader reader = new BufferedReader(new FileReader(new File(path))); + String line; + while((line = reader.readLine()) != null) + file.append(line + '\n'); + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + String source = file.toString(); + int shader = glCreateShader(type); + glShaderSource(shader, source); + glCompileShader(shader); + if(glGetShaderi(shader, GL_COMPILE_STATUS) != 1) + throw new RuntimeException("Failed to compile shader: " + path + "! " + glGetShaderInfoLog(shader)); + return shader; + } + + protected void getUniformLocs() { + projLoc = glGetUniformLocation(program, "projection"); + viewLoc = glGetUniformLocation(program, "view"); + modelLoc = glGetUniformLocation(program, "model"); + } + + public void setProjection(Matrix4f projection) { + glUniformMatrix4fv(projLoc, false, projection.get(new float[16])); + } + + public void setView(Matrix4f view) { + glUniformMatrix4fv(viewLoc, false, view.get(new float[16])); + } + + public void setModel(Matrix4f model) { + glUniformMatrix4fv(modelLoc, false, model.get(new float[16])); + } + + public void enable() { + glUseProgram(program); + } + + public void disable() { + glUseProgram(0); + } +} diff --git a/src/com/gnarly/engine/shaders/Shader2a.java b/src/com/gnarly/engine/shaders/Shader2a.java new file mode 100644 index 0000000..2fd4c9f --- /dev/null +++ b/src/com/gnarly/engine/shaders/Shader2a.java @@ -0,0 +1,24 @@ +package com.gnarly.engine.shaders; + +import static org.lwjgl.opengl.GL20.glGetUniformLocation; +import static org.lwjgl.opengl.GL20.glUniform2f; + +import com.gnarly.engine.texture.Anim; + +public class Shader2a extends Shader { + + private int animLoc; + + protected Shader2a() { + super("res/shaders/s2a/vert.vs", "res/shaders/s2a/frag.fs"); + } + + protected void getUniformLocs() { + super.getUniformLocs(); + animLoc = glGetUniformLocation(program, "animProps"); + } + + public void setAnim(Anim anim) { + glUniform2f(animLoc, anim.getFrameWidth(), anim.getOffset()); + } +} \ No newline at end of file diff --git a/src/com/gnarly/engine/shaders/Shader2c.java b/src/com/gnarly/engine/shaders/Shader2c.java new file mode 100644 index 0000000..650811a --- /dev/null +++ b/src/com/gnarly/engine/shaders/Shader2c.java @@ -0,0 +1,22 @@ +package com.gnarly.engine.shaders; + +import static org.lwjgl.opengl.GL20.glGetUniformLocation; +import static org.lwjgl.opengl.GL20.glUniform4f; + +public class Shader2c extends Shader { + + int colorLoc; + + protected Shader2c() { + super("res/shaders/s2c/vert.vs", "res/shaders/s2c/frag.fs"); + } + + protected void getUniformLocs() { + super.getUniformLocs(); + colorLoc = glGetUniformLocation(program, "iColor"); + } + + public void setColor(float r, float g, float b, float a) { + glUniform4f(colorLoc, r, g, b, a); + } +} \ No newline at end of file diff --git a/src/com/gnarly/engine/shaders/Shader2t.java b/src/com/gnarly/engine/shaders/Shader2t.java new file mode 100644 index 0000000..901b2bc --- /dev/null +++ b/src/com/gnarly/engine/shaders/Shader2t.java @@ -0,0 +1,8 @@ +package com.gnarly.engine.shaders; + +public class Shader2t extends Shader { + + protected Shader2t() { + super("res/shaders/s2t/vert.vs", "res/shaders/s2t/frag.fs"); + } +} \ No newline at end of file diff --git a/src/com/gnarly/engine/shaders/Shader2x.java b/src/com/gnarly/engine/shaders/Shader2x.java new file mode 100644 index 0000000..54188ad --- /dev/null +++ b/src/com/gnarly/engine/shaders/Shader2x.java @@ -0,0 +1,30 @@ +package com.gnarly.engine.shaders; + +import static org.lwjgl.opengl.GL20.glGetUniformLocation; +import static org.lwjgl.opengl.GL20.glUniform2f; +import static org.lwjgl.opengl.GL20.glUniform4f; + +public class Shader2x extends Shader { + + private int charLoc; + private int colorLoc; + + protected Shader2x() { + super("res/shaders/s2x/vert.vs", "res/shaders/s2x/frag.fs"); + } + + protected void getUniformLocs() { + super.getUniformLocs(); + colorLoc = glGetUniformLocation(program, "iColor"); + charLoc = glGetUniformLocation(program, "character"); + } + + public void setColor(float r, float g, float b, float a) { + glUniform4f(colorLoc, r, g, b, a); + } + + public void setChar(int character) { + int column = character % 16, row = character / 16; + glUniform2f(charLoc, (float) column / 16f, (float) row / 16f); + } +} \ No newline at end of file diff --git a/src/com/gnarly/engine/text/Font.java b/src/com/gnarly/engine/text/Font.java new file mode 100644 index 0000000..bcfa957 --- /dev/null +++ b/src/com/gnarly/engine/text/Font.java @@ -0,0 +1,82 @@ +package com.gnarly.engine.text; + +import org.joml.Matrix4f; + +import com.gnarly.engine.display.Camera; +import com.gnarly.engine.model.Vao; +import com.gnarly.engine.shaders.Shader; +import com.gnarly.engine.shaders.Shader2x; +import com.gnarly.engine.texture.Texture; + +public class Font { + + private final int + WIDTH = 5, + HEIGHT = 8; + + private Camera camera; + private Texture texture; + private Shader2x shader; + private Vao vao; + + public Font(Camera camera, String fontPath) { + this.camera = camera; + texture = new Texture(fontPath); + shader = Shader.SHADER2X; + float[] vertices = new float[] { + 0, 0, 0, + 0, HEIGHT, 0, + WIDTH, HEIGHT, 0, + WIDTH, 0, 0 + }; + int[] indices = new int[] { + 0, 1, 3, + 3, 1, 2 + }; + float[] texCoords = new float[] { + 0, 0, + 0, 0.0625f, + 0.0625f, 0.0625f, + 0.0625f, 0 + }; + vao = new Vao(vertices, indices); + vao.addAttribute(texCoords, 2); + } + + public void drawString(String string, float x, float y, float depth, float height, float r, float g, float b, float a) { + float scale = height / (float) HEIGHT; + texture.bind(); + shader.enable(); + shader.setProjection(camera.getProjection()); + shader.setView(camera.getView()); + shader.setColor(r, g, b, a); + char[] chars = string.toCharArray(); + float lx = x; + for (char c : chars) { + if(c == '\n') { + y += (float) (HEIGHT + 1) * scale; + lx = x; + } + else { + if(c != ' ') { + shader.setModel(new Matrix4f().translate(lx, y, depth).scale(scale, scale, 0)); + shader.setChar(c); + vao.render(); + } + lx += (float) (WIDTH + 1) * scale; + } + } + } + + public float getWidth(float height) { + return (height / (float) HEIGHT) * (WIDTH + 1); + } + + public float getCharWidth(float height) { + return (height / (float) HEIGHT) * WIDTH; + } + + public float getHeight(float height) { + return (height / (float) HEIGHT) * (HEIGHT + 1); + } +} diff --git a/src/com/gnarly/engine/texture/Anim.java b/src/com/gnarly/engine/texture/Anim.java new file mode 100644 index 0000000..365eb15 --- /dev/null +++ b/src/com/gnarly/engine/texture/Anim.java @@ -0,0 +1,57 @@ +package com.gnarly.engine.texture; + +public class Anim extends Texture { + + private final float FRAME_WIDTH; + private final long NANO_PER_FRAME; + private final int NUM_FRAMES; + + private int curFrame; + private long startTime; + private boolean playing; + + public Anim(String path, int numFrames, int fps) { + super(path); + FRAME_WIDTH = 1f / (float) numFrames; + NANO_PER_FRAME = 1000000000l / fps; + this.curFrame = 0; + this.NUM_FRAMES = numFrames; + startTime = System.nanoTime(); + playing = true; + } + + @Override + public void bind() { + super.bind(); + if(playing) { + int frame = (int) ((System.nanoTime() - startTime) / NANO_PER_FRAME); + curFrame = frame % NUM_FRAMES; + } + } + + public void play() { + startTime = System.nanoTime(); + playing = true; + } + + public void pause() { + playing = false; + } + + @Override + public int getWidth() { + return width / NUM_FRAMES; + } + + public float getFrameWidth() { + return FRAME_WIDTH; + } + + public float getOffset() { + return FRAME_WIDTH * curFrame; + } + + public void setFrame(int frame) { + curFrame = frame; + } +} diff --git a/src/com/gnarly/engine/texture/Texture.java b/src/com/gnarly/engine/texture/Texture.java new file mode 100644 index 0000000..23f1a63 --- /dev/null +++ b/src/com/gnarly/engine/texture/Texture.java @@ -0,0 +1,83 @@ +package com.gnarly.engine.texture; + +import static org.lwjgl.opengl.GL11.GL_NEAREST; +import static org.lwjgl.opengl.GL11.GL_RGBA; +import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D; +import static org.lwjgl.opengl.GL11.GL_TEXTURE_MAG_FILTER; +import static org.lwjgl.opengl.GL11.GL_TEXTURE_MIN_FILTER; +import static org.lwjgl.opengl.GL11.GL_UNSIGNED_BYTE; +import static org.lwjgl.opengl.GL11.glBindTexture; +import static org.lwjgl.opengl.GL11.glGenTextures; +import static org.lwjgl.opengl.GL11.glTexImage2D; +import static org.lwjgl.opengl.GL11.glTexParameterf; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; + +import javax.imageio.ImageIO; + +import org.lwjgl.BufferUtils; + +public class Texture { + + protected int id, width, height; + + public Texture(String fileName) { + try { + BufferedImage bi = ImageIO.read(new File(fileName)); + width = bi.getWidth(); + height = bi.getHeight(); + + int[] pixelsRaw = new int[width * height]; + pixelsRaw = bi.getRGB(0, 0, width, height, null, 0, width); + + ByteBuffer pixels = BufferUtils.createByteBuffer(width * height * 4); + + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + int pixel = pixelsRaw[i * width + j]; + pixels.put((byte)((pixel >> 16) & 0xFF)); //RED + pixels.put((byte)((pixel >> 8) & 0xFF)); //GREEN + pixels.put((byte)((pixel ) & 0xFF)); //BLUE + pixels.put((byte)((pixel >> 24) & 0xFF)); //ALPHA + } + } + pixels.flip(); + + id = glGenTextures(); + + bind(); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + unbind(); + } catch(IOException e) { + e.printStackTrace(); + } + } + + public Texture(int id) { + this.id = id; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public void bind() { + glBindTexture(GL_TEXTURE_2D, id); + } + + public void unbind() { + glBindTexture(GL_TEXTURE_2D, 0); + } +} diff --git a/src/com/gnarly/game/GamePanel.java b/src/com/gnarly/game/GamePanel.java new file mode 100644 index 0000000..1f7f15a --- /dev/null +++ b/src/com/gnarly/game/GamePanel.java @@ -0,0 +1,116 @@ +package com.gnarly.game; + +import java.io.File; + +import com.gnarly.engine.display.Camera; +import com.gnarly.engine.display.Window; +import com.gnarly.engine.rects.ColRect; +import com.gnarly.engine.rects.TexRect; +import com.gnarly.engine.text.Font; +import com.gnarly.game.board.Map; +import com.gnarly.game.objs.Button; +import com.gnarly.game.text.Console; +import com.gnarly.game.text.EditorManager; +import com.gnarly.game.text.TextBox; + +public class GamePanel { + + private Window window; + private Camera camera; + + private Map map; + private EditorManager editor; + private Button run, stop, menu; + private TexRect header; + + private TextBox background; + private ColRect darkener; + private Button next, smenu; + private Font font; + + private boolean success; + private int level = 1; + private int max; + + private int state = 1; + + public GamePanel(Window window, Camera camera) { + max = new File("res/maps").list().length; + this.window = window; + this.camera = camera; + + map = new Map(camera, "res/maps/level1.llm"); + editor = new EditorManager(window, camera); + run = new Button(window, camera, "res/img/map/buttons/run/unpressed.png", "res/img/map/buttons/run/hovered.png", "res/img/map/buttons/run/pressed.png", 0, 324, 0, 75, 24); + stop = new Button(window, camera, "res/img/map/buttons/stop/unpressed.png", "res/img/map/buttons/stop/hovered.png", "res/img/map/buttons/stop/pressed.png", 76, 324, 0, 76, 24); + menu = new Button(window, camera, "res/img/map/buttons/menu/unpressed.png", "res/img/map/buttons/menu/hovered.png", "res/img/map/buttons/menu/pressed.png", 153, 324, 0, 75, 24); + header = new TexRect(camera, "res/img/header.png", 0, 0, 0, 228, 32); + + background = new TextBox(camera, "", 220, 100, -0.5f, 200, 100, 24); + darkener = new ColRect(camera, 0, 0, -0.25f, 640, 360, 0, 0, 0, 0.5f); + next = new Button(window, camera, "res/img/menu/next/unpressed.png", "res/img/menu/next/hovered.png", "res/img/menu/next/pressed.png", 362, 182, -0.75f, 16, 16); + smenu = new Button(window, camera, "res/img/map/buttons/smenu/unpressed.png", "res/img/map/buttons/smenu/hovered.png", "res/img/map/buttons/smenu/pressed.png", 262, 182, -0.75f, 16, 16); + font = new Font(camera, "res/img/fonts/default.png"); + } + + public void update() { + if(!success) { + editor.update(); + map.update(); + run.update(); + stop.update(); + menu.update(); + if(run.getState() == Button.RELEASED) { + Console.error.clear(); + map.execute(editor.getCommands()); + } + else if(stop.getState() == Button.RELEASED) + map.fullStop(); + else if(menu.getState() == Button.RELEASED) + state = 0; + success = map.success(); + if(success) { + ++level; + if(level > max) + level = 1; + } + } + else { + next.update(); + smenu.update(); + if(next.getState() == Button.RELEASED) { + map = new Map(camera, "res/maps/level" + level + ".llm"); + editor = new EditorManager(window, camera); + success = false; + } + else if(smenu.getState() == Button.RELEASED) { + map = new Map(camera, "res/maps/level" + level + ".llm"); + editor = new EditorManager(window, camera); + success = false; + state = 0; + } + } + } + + public void render() { + header.render(); + run.render(); + stop.render(); + menu.render(); + map.render(); + editor.render(); + if(success) { + font.drawString("SUCCESS!", (640 - font.getWidth(24) * 7 - font.getCharWidth(24)) / 2, 106, -0.75f, 24, 1, 1, 1, 1); + darkener.render(); + background.render(); + next.render(); + smenu.render(); + } + } + + public int getState() { + int temp = state; + state = 1; + return temp; + } +} diff --git a/src/com/gnarly/game/Main.java b/src/com/gnarly/game/Main.java new file mode 100644 index 0000000..4491d5e --- /dev/null +++ b/src/com/gnarly/game/Main.java @@ -0,0 +1,90 @@ +package com.gnarly.game; + +import com.gnarly.engine.display.Camera; +import com.gnarly.engine.display.Window; + +public class Main implements Runnable { + + public static final int BIG_TICK = 8; + + private final int TICK_RATE = 64; + private final int FPS = 59; + + public static int tick = 0; + public static int fps = 0; + + private int state; + + private Window window; + private Camera camera; + private MenuPanel menu; + private GamePanel panel; + + public void start() { + Thread gameLoop = new Thread(this, "OpenGL"); + gameLoop.start(); + } + + public void run() { + long pastTime, curTime, npf = 1000000000 / FPS, pastSec, pastUTime, npt = 1000000000 / TICK_RATE; + int frames = 0; + init(); + pastTime = System.nanoTime(); + pastUTime = pastTime; + pastSec = pastTime; + while(!window.shouldClose()) { + curTime = System.nanoTime(); + while(curTime - pastUTime >= npt) { + update(); + pastUTime += npt; + ++tick; + } + curTime = System.nanoTime(); + if(curTime - pastTime > npf) { + render(); + pastTime += npf; + ++frames; + } + if(curTime - pastSec >= 1000000000) { + fps = frames; + frames = 0; + pastSec += 1000000000; + } + } + } + + private void init() { + state = 0; + window = new Window(false); + camera = new Camera(640, 360); + menu = new MenuPanel(window, camera); + panel = new GamePanel(window, camera); + window.bind(); + } + + private void update() { + window.update(); + if(state == 0) { + menu.update(); + state = menu.getState(); + } + else { + panel.update(); + state = panel.getState(); + } + } + + private void render() { + window.clear(); + if(state == 0) + menu.render(); + else + panel.render(); + window.bind(); + window.swap(); + } + + public static void main(String[] args) { + new Main().start(); + } +} diff --git a/src/com/gnarly/game/MenuPanel.java b/src/com/gnarly/game/MenuPanel.java new file mode 100644 index 0000000..531e139 --- /dev/null +++ b/src/com/gnarly/game/MenuPanel.java @@ -0,0 +1,104 @@ +package com.gnarly.game; + +import com.gnarly.engine.display.Camera; +import com.gnarly.engine.display.Window; +import com.gnarly.engine.rects.TexRect; +import com.gnarly.engine.text.Font; +import com.gnarly.game.objs.Button; + +public class MenuPanel { + + private Window window; + private Camera camera; + + private Font font; + + private Button play, help, close, next, prev; + + private TexRect background; + private TexRect helpMenu; + + private int state = 0, page = 0; + private boolean showHelp = false; + private String[] headers, content; + + + public MenuPanel(Window window, Camera camera) { + this.window = window; + this.camera = camera; + font = new Font(camera, "res/img/fonts/default.png"); + background = new TexRect(camera, "res/img/menu/background.png", 0, 0, 0, 640, 360); + play = new Button(window, camera, "res/img/menu/play/unpressed.png", "res/img/menu/play/hovered.png", "res/img/menu/play/pressed.png", 260, 160, 0, 120, 32); + help = new Button(window, camera, "res/img/menu/help/unpressed.png", "res/img/menu/help/hovered.png", "res/img/menu/help/pressed.png", 260, 200, 0, 120, 32); + close = new Button(window, camera, "res/img/menu/close/unpressed.png", "res/img/menu/close/hovered.png", "res/img/menu/close/pressed.png", 384, 110, 0, 16, 16); + next = new Button(window, camera, "res/img/menu/next/unpressed.png", "res/img/menu/next/hovered.png", "res/img/menu/next/pressed.png", 384, 264, 0, 16, 16); + prev = new Button(window, camera, "res/img/menu/prev/unpressed.png", "res/img/menu/prev/hovered.png", "res/img/menu/prev/pressed.png", 240, 264, 0, 16, 16); + helpMenu = new TexRect(camera, "res/img/menu/help/helpMenu.png", 240, 110, 0, 160, 170); + headers = new String[8]; + headers[0] = "OVERVIEW"; + headers[1] = "GOAL"; + headers[2] = "PROGRAMMING"; + headers[3] = "FIRE"; + headers[4] = "ROTATE"; + headers[5] = "WAIT"; + headers[6] = "THREADS"; + headers[7] = "TIMING"; + content = new String[8]; + content[0] = "SO WHAT THE HECK IS THIS GAME?\n\nWELL BASICALLY ITS A PROGRAMMING\nGAME IN WHICH THE PLAYER CONTROLS\nMIRRORS IN ORDER TO DIRECT A LASER TO\nA TRIGGER."; + content[1] = "THE GOAL OF THE GAME IS TO BEAT\nALL THE LEVELS. THE EXACT GOAL\nOF EACH LEVEL VARIES BUT THE GOAL\nIS ALWAYS ABOUT TRIGGERING A TRIGGER\nWITH A LASER. WHICH TRIGGER AND AT\nWHAT TIMES ARE LEVEL SPECIFIC.\nTHE BOX AT THE TOP WILL TELL\nYOU THE GOAL FOR THAT LEVEL"; + content[2] = "TO CONTROL THE GAME BOARD YOU USE THE\nPROGRAMMING INTRFACE ON THE LEFT.\n\nTHERE ARE THREE COMMANDS YOU CAN USE.\n\n- FIRE\n- ROTATE\n- WAIT"; + content[3] = "THE FIRE COMMAND DOES EXACTLY WHAT\nIT SAYS. IT FIRES A LASER.\n\nUSAGE:\n\nFIRE X\n\n - X IS THE X COMPONENT OF\n THE LOCATION OF THE\n CANNON YOU WANT TO FIRE."; + content[4] = "THE ROTATE COMMAND ROTATES A MIRROR\n90 DEGREES.\n\nUSAGE:\n\nROTATE X Y\n\n - X IS THE X COMPONENT OF\n THE LOCATION OF THE\n THE MIRROR YOU WANT ROTATED\n\n - Y IS THE Y COMPONENT OF\n THE LOCATION OF THE\n THE MIRROR YOU WANT ROTATED"; + content[5] = "THE WAIT COMMAND PAUSES THE CURRENT\nTHREAD FOR A CERTAIN NUMBER FO TICKS.\n\nUSAGE:\n\nWAIT X\n\n - X IS THE NUMBER OF TICKS\n YOU WANT THE THREAD TO PAUSE"; + content[6] = "YOU HAVE FOUR THREADS AT YOUR DISPOAL.\nEACH THREAD CAN ONLY HOUSE A\nCERTAIN NUMBER OF COMMANDS.\nTHE MAIN ADVANTAGE OF MUTIPLE THREADS\nIS IT ALLOWS YOU TO RUN MUTIPLE\nCOMMANDS SIMULTANEOUSLY."; + content[7] = "THE WHOLE GAME RUNS ON A SYSTEM\nOF TICKS. EACH TICK IS 1/16TH\nOF A SECOND. DURING ONE TICK\nONE COMMAND FROM EACH THREAD\nIS EXECUTED."; + } + + public void update() { + if(!showHelp) { + play.update(); + help.update(); + if(play.getState() == Button.RELEASED) + state = 1; + else if(help.getState() == Button.RELEASED) + showHelp = true; + } + else { + close.update(); + next.update(); + prev.update(); + if(close.getState() == Button.RELEASED) { + page = 0; + showHelp = false; + } + else if(next.getState() == Button.RELEASED && page < headers.length - 1) + ++page; + else if(prev.getState() == Button.RELEASED && page > 0) + --page; + } + } + + public void render() { + if(!showHelp) { + play.render(); + help.render(); + } + else { + close.render(); + next.render(); + prev.render(); + String pageString = "[" + (page + 1) + "/" + headers.length + "]"; + font.drawString(pageString, (640 - (pageString.length() * 5 + pageString.length() - 1)) / 2, 269, 0, 8, 1, 1, 1, 1); + font.drawString(headers[page], 243, 113, 0, 16, 1, 1, 1, 1); + font.drawString(content[page], 243, 136, 0, 5.3333333333f, 1, 1, 1, 1); + helpMenu.render(); + } + background.render(); + } + + public int getState() { + int temp = state; + state = 0; + return temp; + } +} diff --git a/src/com/gnarly/game/board/Map.java b/src/com/gnarly/game/board/Map.java new file mode 100644 index 0000000..3c0049f --- /dev/null +++ b/src/com/gnarly/game/board/Map.java @@ -0,0 +1,308 @@ +package com.gnarly.game.board; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.Scanner; + +import org.joml.Vector3f; + +import com.gnarly.engine.display.Camera; +import com.gnarly.engine.rects.TexRect; +import com.gnarly.game.Main; +import com.gnarly.game.board.laser.Cannon; +import com.gnarly.game.board.laser.Laser; +import com.gnarly.game.board.laser.Trigger; +import com.gnarly.game.commands.Command; +import com.gnarly.game.commands.Fire; +import com.gnarly.game.commands.Rotate; +import com.gnarly.game.commands.Wait; +import com.gnarly.game.text.Console; +import com.gnarly.game.text.TextBox; + +public class Map { + + public static final int + EMPTY = 0, + M45 = 1, + M135 = 2, + CANNON = 3, + TRIGGER = 4; + + private static final int + OFFSET = 14, + WIDTH = 32, + HEIGHT = 32, + COLUMNS = 12, + ROWS = 10; + + private String mapPath; + + private Console console; + + private Camera camera; + private TexRect[] states; + private int map[][]; + + private TexRect topbar, leftbar; + private TexRect[] corners, nums; + + private float x, y; + + private TextBox ghead, goals; + + private ArrayList lasers; + private ArrayList cannons; + private ArrayList triggers; + + private boolean execute, lastRan; + private ArrayList[] commands; + + private boolean success = false; + + public Map(Camera camera, String mapPath) { + this.mapPath = mapPath; + this.camera = camera; + this.x = camera.getWidth() - getWidth(); + this.y = 0; + ghead = new TextBox(camera, "GOALS\nTRIG |TICK", 182, 33, 0, 46, 15, 5.3333333f); + goals = new TextBox(camera, "", 182, 49, 0, 46, 185, 5.3333333f); + map = new int[COLUMNS][ROWS]; + states = new TexRect[5]; + states[EMPTY] = new TexRect(camera, "res/img/map/empty.png", 0, 0, 0, 32, 32); + states[M45] = new TexRect(camera, "res/img/map/mirror45.png", 0, 0, 0, 32, 32); + states[M135] = new TexRect(camera, "res/img/map/mirror135.png", 0, 0, 0, 32, 32); + states[CANNON] = new TexRect(camera, "res/img/map/cannon.png", 10, 8, 0, 0, 0, 32, 32); + states[TRIGGER] = new TexRect(camera, "res/img/map/trigger.png", 32, 32, 0, 0, 0, 32, 32); + states[TRIGGER].pause(); + corners = new TexRect[4]; + corners[0] = new TexRect(camera, "res/img/bars/tlcorner.png", x, y, 0, OFFSET, OFFSET); + corners[1] = new TexRect(camera, "res/img/bars/blcorner.png", x, y + getHeight() - OFFSET, 0, OFFSET, OFFSET); + corners[2] = new TexRect(camera, "res/img/bars/brcorner.png", x + getWidth()- OFFSET, y + getHeight() - OFFSET, 0, OFFSET, OFFSET); + corners[3] = new TexRect(camera, "res/img/bars/trcorner.png", x + getWidth()- OFFSET, y, 0, OFFSET, OFFSET); + topbar = new TexRect(camera, "res/img/bars/topbar.png", 0, 0, 0, WIDTH, OFFSET); + leftbar = new TexRect(camera, "res/img/bars/leftbar.png", 0, 0, 0, OFFSET, HEIGHT); + lasers = new ArrayList<>(); + cannons = new ArrayList<>(); + triggers = new ArrayList<>(); + nums = new TexRect[12]; + for (int i = 0; i < nums.length; i++) + nums[i] = new TexRect(camera, "res/img/bars/numbers/" + i + ".png", 0, 0, 0, 8, 8); + loadMap(); + console = new Console(camera); + } + + public void update() { + if(Main.tick % Main.BIG_TICK == 0) { + boolean ran = false; + if(execute || lasers.size() > 0) { + ran = true; + if(commands != null) { + String[] commandStrings = new String[4]; + for (int i = 0; i < commandStrings.length; i++) + commandStrings[i] = new String(); + for (int i = 0; i < 4; i++) { + if(commands[i].size() > 0) { + commandStrings[i] = commands[i].get(0).getCommand(); + Command command = commands[i].get(0); + if(command instanceof Wait) { + Wait wait = (Wait) command; + wait.decrement(); + if(wait.getTicks() == 0) + commands[i].remove(0); + } + else if(command instanceof Rotate) { + Rotate rotate = (Rotate) command; + int x = rotate.getX(); + int y = rotate.getY() + 1; + if(map[x][y] == M45) + map[x][y] = M135; + else if(map[x][y] == M135) + map[x][y] = M45; + else { + Console.error.add("LOCATION " + x + " " + y + " IS NOT A MIRROR"); + execute = false; + } + commands[i].remove(0); + } + else if(command instanceof Fire) { + Fire fire = (Fire) command; + fire(fire.getCannon()); + commands[i].remove(0); + } + } + } + int num = 0; + for (int i = 0; i < 4; i++) + if(commands[i].size() > 0) + ++num; + if(num == 0) { + commands = null; + execute = false; + } + console.printCommands(commandStrings); + } + } + if(!ran && lastRan) { + for (int i = 0; i < triggers.size(); i++) { + triggers.get(i).update(); + if(!triggers.get(i).success()) + Console.error.add("FAILED TO MEET ALL TRIGGER CONDITIONS!"); + } + if(Console.error.size() == 0) + success = true; + for (int i = 0; i < triggers.size(); i++) + triggers.get(i).reset(); + fullStop(); + } + lastRan = ran; + } + for (int i = 0; i < lasers.size(); i++) + lasers.get(i).update(); + for (int i = 0; i < lasers.size(); i++) + if(lasers.get(i).done()) + lasers.remove(i); + if(execute || lasers.size() > 0) + for (int i = 0; i < triggers.size(); i++) + triggers.get(i).update(); + } + + public void render() { + ghead.render(); + goals.render(); + console.render(); + for (int i = 0; i < lasers.size(); i++) + lasers.get(i).render(); + for (int i = 0; i < corners.length; i++) + corners[i].render(); + for (int i = 0; i < map.length; i++) { + nums[i].setPos(x + i * WIDTH + OFFSET + 12, y + 3); + nums[i].render(); + topbar.setPos(x + i * WIDTH + OFFSET, y); + topbar.render(); + nums[i].setPos(x + i * WIDTH + OFFSET + 12, y + getHeight() - OFFSET + 3); + nums[i].render(); + topbar.setPos(x + i * WIDTH + OFFSET, y + getHeight() - OFFSET); + topbar.render(); + } + for (int i = 0; i < map[0].length; i++) { + if (i > 0 && i < map[0].length - 1) { + nums[i - 1].setPos(x + 3, y + i * HEIGHT + OFFSET + 12); + nums[i - 1].render(); + nums[i - 1].setPos(x + getWidth() - OFFSET + 3, y + i * HEIGHT + OFFSET + 12); + nums[i - 1].render(); + } + leftbar.setPos(x, y + i * HEIGHT + OFFSET); + leftbar.render(); + leftbar.setPos(x + getWidth() - OFFSET, y + i * HEIGHT + OFFSET); + leftbar.render(); + } + for (int i = 0; i < map.length; i++) { + for (int j = 0; j < map[0].length; j++) { + int state = map[i][j]; + if(state != TRIGGER) { + states[state].setPos(x + i * WIDTH + OFFSET, y + j * HEIGHT + OFFSET); + states[state].render(); + } + } + } + for (int i = 0; i < triggers.size(); i++) { + Trigger trigger = triggers.get(i); + states[TRIGGER].setFrame(trigger.getFrame()); + states[TRIGGER].setPos(x + trigger.getX() * WIDTH + OFFSET, y + trigger.getY() * HEIGHT + OFFSET); + states[TRIGGER].render(); + } + } + + public static float getWidth() { + return WIDTH * COLUMNS + OFFSET * 2; + } + + public static float getHeight() { + return HEIGHT * ROWS + OFFSET * 2; + } + + private void fire(int cannonNum) { + Cannon cannon = null; + for (int i = 0; i < cannons.size() && cannon == null; i++) { + if(cannons.get(i).getX() == cannonNum) + cannon = cannons.get(i); + } + if(cannon == null) + Console.error.add("NO CANNON AT POSITION " + cannonNum + "!"); + else { + int x = cannon.getX(); + int y = cannon.getY(); + Vector3f dir = cannon.getDir(); + x += dir.x; + y += dir.y; + lasers.add(new Laser(camera, dir, this.x + OFFSET, this.y + OFFSET, x, y, map, triggers)); + } + } + + private void loadMap() { + Scanner scanner = null; + try { + triggers.clear(); + cannons.clear(); + scanner = new Scanner(new File(mapPath)); + for (int j = 0; j < this.map[0].length; j++) { + for (int i = 0; i < this.map.length; i++) { + this.map[i][j] = scanner.nextInt(); + if(this.map[i][j] == CANNON) + cannons.add(new Cannon(i, j, Laser.UP)); + if(this.map[i][j] == TRIGGER) + triggers.add(new Trigger(i, j)); + } + } + int numGoals = scanner.nextInt(); + String goals = new String(); + for (int i = 0; i < numGoals; i++) { + String curGoal = new String(); + int trigger = scanner.nextInt(); + int tickNum = scanner.nextInt(); + curGoal += triggers.get(trigger).getX(); + while(curGoal.length() < 4) + curGoal += ' '; + curGoal += " |"; + curGoal += tickNum + "\n"; + goals += curGoal; + triggers.get(trigger).addCondition(tickNum); + } + this.goals.setText(goals); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } finally { + scanner.close(); + } + } + + public void execute(ArrayList[] commands) { + if(Console.error.size() == 0) { + lasers.clear(); + console.clear(); + execute = true; + this.commands = commands; + } + } + + public void fullStop() { + loadMap(); + lasers.clear(); + execute = false; + commands = null; + } + + public void setMap(String mapPath) { + this.mapPath = mapPath; + loadMap(); + } + + public int[][] getMap() { + return map; + } + + public boolean success() { + return success; + } +} diff --git a/src/com/gnarly/game/board/laser/Cannon.java b/src/com/gnarly/game/board/laser/Cannon.java new file mode 100644 index 0000000..04f350f --- /dev/null +++ b/src/com/gnarly/game/board/laser/Cannon.java @@ -0,0 +1,27 @@ +package com.gnarly.game.board.laser; + +import org.joml.Vector3f; + +public class Cannon { + + int x, y; + Vector3f dir; + + public Cannon(int x, int y, Vector3f dir) { + this.x = x; + this.y = y; + this.dir = dir; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public Vector3f getDir() { + return dir; + } +} diff --git a/src/com/gnarly/game/board/laser/Laser.java b/src/com/gnarly/game/board/laser/Laser.java new file mode 100644 index 0000000..71920d8 --- /dev/null +++ b/src/com/gnarly/game/board/laser/Laser.java @@ -0,0 +1,182 @@ +package com.gnarly.game.board.laser; + +import java.util.ArrayList; + +import org.joml.Vector3f; + +import com.gnarly.engine.display.Camera; +import com.gnarly.engine.rects.TexRect; +import com.gnarly.engine.texture.Anim; +import com.gnarly.game.Main; +import com.gnarly.game.board.Map; + +public class Laser { + + public static final Vector3f + UP = new Vector3f(0, -1, 0), + DOWN = new Vector3f(0, 1, 0), + LEFT = new Vector3f(-1, 0, 0), + RIGHT = new Vector3f(1, 0, 0); + + private final int + STRAIGHT = 0, + BEND = 1, + TRIGGER = 2, + SOLID = 3; + + private Anim[] fanims; + private TexRect[] frects; + private Anim[] banims; + private TexRect[] brects; + + private float startX, startY; + + private int fx, fy, ftype, frame; + private int[] props; + private Vector3f fdir; + + private int bx, by, btype; + + private boolean renderFront, renderBack; + + private ArrayList triggers; + private int[][] map; + + public Laser(Camera camera, Vector3f dir, float startX, float startY, int x, int y, int[][] map, ArrayList triggers) { + this.startX = startX; + this.startY = startY; + bx = 0; + by = 0; + fx = x; + fy = y; + fdir = dir; + this.map = map; + this.triggers = triggers; + renderFront = true; + renderBack = false; + initAnims(camera); + } + + private void initAnims(Camera camera) { + banims = new Anim[2]; + banims[0] = new Anim("res/img/map/laser/laserStraightBack.png", 32, 64); + banims[1] = new Anim("res/img/map/laser/laserBendBack.png", 32, 64); + brects = new TexRect[2]; + brects[STRAIGHT] = new TexRect(camera, banims[0], 0, 0, 0, 32, 32); + brects[BEND] = new TexRect(camera, banims[1], 0, 0, 0, 32, 32); + for (int i = 0; i < banims.length; i++) + banims[i].pause(); + fanims = new Anim[2]; + fanims[0] = new Anim("res/img/map/laser/laserStraight.png", 32, 64); + fanims[1] = new Anim("res/img/map/laser/laserBend.png", 32, 64); + frects = new TexRect[2]; + frects[STRAIGHT] = new TexRect(camera, fanims[0], 0, 0, 0, 32, 32); + frects[BEND] = new TexRect(camera, fanims[1], 0, 0, 0, 32, 32); + for (int i = 0; i < fanims.length; i++) + fanims[i].pause(); + } + + public void update() { + frame = Main.tick % Main.BIG_TICK * (32 / Main.BIG_TICK); + if(frame == 0) { + if(bx < 0 || by < 0 || bx >= map.length || by >= map[0].length || getType(bx, by) >= TRIGGER) + renderBack = false; + else if(props != null) { + btype = ftype; + brects[btype].setPos(startX + bx * 32, startY + by * 32); + brects[btype].setRotation(props[0]); + brects[btype].setFlip(1, props[1]); + renderBack = true; + } + if(fx < 0 || fy < 0 || fx >= map.length || fy >= map[0].length || getType(fx, fy) == SOLID) + renderFront = false; + else if(getType(fx, fy) == TRIGGER) { + for (int i = 0; i < triggers.size(); i++) { + if(triggers.get(i).getX() == fx && triggers.get(i).getY() == fy) + triggers.get(i).trigger(); + } + renderFront = false; + } + else { + ftype = getType(fx, fy); + props = getProps(fx, fy, fdir); + frects[ftype].setPos(startX + fx * 32, startY + fy * 32); + frects[ftype].setRotation(props[0]); + frects[ftype].setFlip(1, props[1]); + fdir = getDir(fx, fy, fdir); + } + bx = fx; + by = fy; + fx += fdir.x; + fy += fdir.y; + } + } + + private int getType(int x, int y) { + if(map[x][y] == 0) + return STRAIGHT; + else if(map[x][y] >= 1 && map[x][y] <= 2) + return BEND; + else if(map[x][y] == 4) + return TRIGGER; + else + return SOLID; + } + + private int[] getProps(int x, int y, Vector3f dir) { + if((dir.x == 1 && dir.y == 0 && map[x][y] == Map.M45)) + return new int[] {0, 1}; + else if((dir.x == 1 && dir.y == 0 && map[x][y] == Map.M135)) + return new int[] {0, -1}; + else if((dir.x == 1 && dir.y == 0 && map[x][y] == Map.EMPTY)) + return new int[] {90, 1}; + else if((dir.x == -1 && dir.y == 0 && map[x][y] == Map.M45)) + return new int[] {180, 1}; + else if((dir.x == -1 && dir.y == 0 && map[x][y] == Map.M135)) + return new int[] {180, -1}; + else if((dir.x == -1 && dir.y == 0 && map[x][y] == Map.EMPTY)) + return new int[] {90, -1}; + else if((dir.x == 0 && dir.y == 1 && map[x][y] == Map.M45)) + return new int[] {90, -1}; + else if((dir.x == 0 && dir.y == 1 && map[x][y] == Map.M135)) + return new int[] {90, 1}; + else if((dir.x == 0 && dir.y == 1 && map[x][y] == Map.EMPTY)) + return new int[] {0, -1}; + else if((dir.x == 0 && dir.y == -1 && map[x][y] == Map.M45)) + return new int[] {270, -1}; + else if((dir.x == 0 && dir.y == -1 && map[x][y] == Map.M135)) + return new int[] {270, 1}; + else if((dir.x == 0 && dir.y == -1 && map[x][y] == Map.EMPTY)) + return new int[] {0, 1}; + return null; + } + + private Vector3f getDir(int x, int y, Vector3f dir) { + if((dir.x == 0 && dir.y == 1 && map[x][y] == Map.M45) || (dir.x == 0 && dir.y == -1 && map[x][y] == Map.M135)) + return LEFT; + else if((dir.x == 0 && dir.y == 1 && map[x][y] == Map.M135) || (dir.x == 0 && dir.y == -1 && map[x][y] == Map.M45)) + return RIGHT; + else if((dir.x == -1 && dir.y == 0 && map[x][y] == Map.M135) || (dir.x == 1 && dir.y == 0 && map[x][y] == Map.M45)) + return UP; + else if((dir.x == -1 && dir.y == 0 && map[x][y] == Map.M45) || (dir.x == 1 && dir.y == 0 && map[x][y] == Map.M135)) + return DOWN; + else if(map[x][y] == Map.EMPTY) + return dir; + return null; + } + + public void render() { + if(renderBack) { + banims[btype].setFrame(frame); + brects[btype].render(); + } + if(renderFront) { + fanims[ftype].setFrame(frame); + frects[ftype].render(); + } + } + + public boolean done() { + return !renderBack && !renderFront; + } +} diff --git a/src/com/gnarly/game/board/laser/Trigger.java b/src/com/gnarly/game/board/laser/Trigger.java new file mode 100644 index 0000000..617e00d --- /dev/null +++ b/src/com/gnarly/game/board/laser/Trigger.java @@ -0,0 +1,79 @@ +package com.gnarly.game.board.laser; + +import java.util.ArrayList; + +import com.gnarly.game.Main; +import com.gnarly.game.text.Console; + +public class Trigger { + + private int tick = 0; + private int frame = 0; + private int triggered = 0; + + private int x, y, met; + + private ArrayList conditions; + + public Trigger(int x, int y) { + this.x = x; + this.y = y; + conditions = new ArrayList<>(); + } + + public void update() { + if(triggered == 2) { + frame = Main.tick % Main.BIG_TICK * (32 / Main.BIG_TICK); + if(Main.tick % Main.BIG_TICK == Main.BIG_TICK - 1) + --triggered; + } + else + frame = 0; + if(Main.tick % Main.BIG_TICK == 0) { + boolean check = false; + for (int i = 0; i < conditions.size(); i++) { + if(conditions.get(i).intValue() == tick) { + check = true; + if(triggered == 0) + Console.error.add("TRIGGER FAILED TO BE TRIGGERED AT PROPER TIME!"); + else + ++met; } + } + if(!check && triggered == 2) { + --met; + Console.error.add("TRIGGER WAS TRIGGERED AT AN UNEXPECTED TIME!"); + } + ++tick; + } + } + + public void trigger() { + triggered = 2; + } + + public int getFrame() { + return frame; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public void addCondition(int condition) { + conditions.add(condition); + } + + public void reset() { + conditions.clear(); + tick = 0; + met = 0; + } + + public boolean success() { + return met == conditions.size(); + } +} diff --git a/src/com/gnarly/game/commands/Command.java b/src/com/gnarly/game/commands/Command.java new file mode 100644 index 0000000..f7cdb39 --- /dev/null +++ b/src/com/gnarly/game/commands/Command.java @@ -0,0 +1,5 @@ +package com.gnarly.game.commands; + +public interface Command { + public String getCommand(); +} diff --git a/src/com/gnarly/game/commands/Fire.java b/src/com/gnarly/game/commands/Fire.java new file mode 100644 index 0000000..7c79d48 --- /dev/null +++ b/src/com/gnarly/game/commands/Fire.java @@ -0,0 +1,18 @@ +package com.gnarly.game.commands; + +public class Fire implements Command { + + private int cannon; + + public Fire(int cannon) { + this.cannon = cannon; + } + + public int getCannon() { + return cannon; + } + + public String getCommand() { + return "FIRE " + cannon; + } +} diff --git a/src/com/gnarly/game/commands/Rotate.java b/src/com/gnarly/game/commands/Rotate.java new file mode 100644 index 0000000..5f13c00 --- /dev/null +++ b/src/com/gnarly/game/commands/Rotate.java @@ -0,0 +1,23 @@ +package com.gnarly.game.commands; + +public class Rotate implements Command { + + private int x, y; + + public Rotate(int x, int y) { + this.x = x; + this.y = y; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public String getCommand() { + return "ROTATE " + x + " " + y; + } +} diff --git a/src/com/gnarly/game/commands/Wait.java b/src/com/gnarly/game/commands/Wait.java new file mode 100644 index 0000000..03e30d6 --- /dev/null +++ b/src/com/gnarly/game/commands/Wait.java @@ -0,0 +1,22 @@ +package com.gnarly.game.commands; + +public class Wait implements Command { + + private int ticks; + + public Wait(int ticks) { + this.ticks = ticks; + } + + public int getTicks() { + return ticks; + } + + public void decrement() { + --ticks; + } + + public String getCommand() { + return "WAIT " + ticks; + } +} diff --git a/src/com/gnarly/game/objs/Button.java b/src/com/gnarly/game/objs/Button.java new file mode 100644 index 0000000..1b7bb1f --- /dev/null +++ b/src/com/gnarly/game/objs/Button.java @@ -0,0 +1,77 @@ +package com.gnarly.game.objs; + +import static org.lwjgl.glfw.GLFW.GLFW_MOUSE_BUTTON_1; + +import org.joml.Vector3f; + +import com.gnarly.engine.display.Camera; +import com.gnarly.engine.display.Window; +import com.gnarly.engine.rects.TexRect; + +public class Button { + + public static final int + UNPRESSED = 0, + RELEASED = 1, + PRESSED = 2, + HELD = 3; + + private Window window; + private Camera camera; + + private TexRect[] states; + + private float x, y, width, height; + + private int state, tex; + + public Button(Window window, Camera camera, String tex1, String tex2, String tex3, float x, float y, float depth, float width, float height) { + this.window = window; + this.camera = camera; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + states = new TexRect[3]; + states[0] = new TexRect(camera, tex1, x, y, depth, width, height); + states[1] = new TexRect(camera, tex2, x, y, depth, width, height); + states[2] = new TexRect(camera, tex3, x, y, depth, width, height); + tex = 0; + state = 0; + } + + public void update() { + if(contains(window.getMouseCoords(camera))) { + if(window.mousePressed(GLFW_MOUSE_BUTTON_1)) { + tex = 2; + if(state <= RELEASED) + state = PRESSED; + else + state = HELD; + } + else { + tex = 1; + if(state >= PRESSED) + state = RELEASED; + else + state = UNPRESSED; + } + } + else { + tex = 0; + state = UNPRESSED; + } + } + + public void render() { + states[tex].render(); + } + + public boolean contains(Vector3f coords) { + return coords.x >= x && coords.y >= y && coords.x < x + width && coords.y < y + height; + } + + public int getState() { + return state; + } +} diff --git a/src/com/gnarly/game/text/Console.java b/src/com/gnarly/game/text/Console.java new file mode 100644 index 0000000..0e77ae8 --- /dev/null +++ b/src/com/gnarly/game/text/Console.java @@ -0,0 +1,79 @@ +package com.gnarly.game.text; + +import java.util.ArrayList; + +import com.gnarly.engine.display.Camera; +import com.gnarly.engine.rects.ColRect; +import com.gnarly.engine.text.Font; + +public class Console { + + public static ArrayList error = new ArrayList<>(); + + private Camera camera; + + private ColRect background; + private ColRect body; + + private Font font; + + private float x, y, width, height; + + ArrayList console; + + private int maxLength; + private int maxHeight; + private int viewY; + + public Console(Camera camera) { + this.camera = camera; + x = 0; + y = 235; + width = 228; + height = 88; + viewY = 0; + background = new ColRect(camera, x, y, 0, width, height, 1, 1, 1, 1); + body = new ColRect(camera, x + 1, y + 1, 0, width - 2, height - 2, 0, 0, 0, 1); + font = new Font(camera, "res/img/fonts/default.png"); + console = new ArrayList(); + maxLength = (int) ((width - 2) / font.getWidth(4)); + maxHeight = (int) ((height - 2) / 5); + } + + public void render() { + if(error.size() > 0) + for (int i = 0; i < error.size(); i++) + font.drawString(error.get(i), x + 3, y + i * 5 + 3, 0, 4, 1, 0, 0, 1); + else + for (int i = viewY; i < console.size() && i < viewY + maxHeight; i++) + font.drawString(console.get(i), x + 3, y + (i - viewY) * 5 + 3, 0, 4, 1, 1, 1, 1); + body.render(); + background.render(); + } + + public void printCommands(String[] commands) { + int perCommand = (int) Math.floor((maxLength - 6) / 4); + String line = new String(); + for (int i = 0; i < commands.length; i++) { + String command = commands[i]; + while(command.length() < perCommand) + command += ' '; + if(i != 3) + command += "| "; + line += command; + } + println(line); + } + + public void println(String line) { + console.add(line); + if(console.size() > maxHeight && viewY + maxHeight + 1 == console.size()) + ++viewY; + } + + public void clear() { + viewY = 0; + error.clear(); + console.clear(); + } +} diff --git a/src/com/gnarly/game/text/EditorManager.java b/src/com/gnarly/game/text/EditorManager.java new file mode 100644 index 0000000..0ceb86b --- /dev/null +++ b/src/com/gnarly/game/text/EditorManager.java @@ -0,0 +1,85 @@ +package com.gnarly.game.text; + +import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_CONTROL; +import static org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT_CONTROL; +import static org.lwjgl.glfw.GLFW.GLFW_KEY_TAB; +import static org.lwjgl.glfw.GLFW.GLFW_MOUSE_BUTTON_1; + +import java.util.ArrayList; + +import org.joml.Vector3f; + +import com.gnarly.engine.display.Camera; +import com.gnarly.engine.display.Window; +import com.gnarly.engine.text.Font; +import com.gnarly.game.board.Map; +import com.gnarly.game.commands.Command; + +public class EditorManager { + + private Window window; + private Camera camera; + + private Font font; + private StringBuilder input; + private TextEditor[] boxes; + + private int focus; + + public EditorManager(Window window, Camera camera) { + this.window = window; + this.camera = camera; + float x = 0, y = 33, width = 180, height = 200; + font = new Font(camera, "res/img/fonts/default.png"); + input = new StringBuilder(); + focus = 0; + boxes = new TextEditor[4]; + boxes[0] = new TextEditor(window, camera, font, "THREAD 1", x, y, 0, width / 2f, height / 2f); + boxes[1] = new TextEditor(window, camera, font, "THREAD 2", x + width / 2 + 1, y, 0, width / 2f, height / 2f); + boxes[2] = new TextEditor(window, camera, font, "THREAD 3", x, y + height / 2 + 1, 0, width / 2f, height / 2f); + boxes[3] = new TextEditor(window, camera, font, "THREAD 4", x + width / 2 + 1, y + height / 2 + 1, 0, width / 2f, height / 2f); + } + + public void update() { + if((window.getKey(GLFW_KEY_LEFT_CONTROL) >= Window.PRESSED || window.getKey(GLFW_KEY_RIGHT_CONTROL) >= Window.PRESSED) && window.getKey(GLFW_KEY_TAB) == Window.PRESSED && focus != -1) { + ++focus; + if(focus == 4) + focus = 0; + for (int i = 0; i < boxes.length; i++) { + if(i == focus) + boxes[i].setActive(new Vector3f(0, 0, 0)); + else + boxes[i].hideCursor(); + } + } + if(window.mousePressed(GLFW_MOUSE_BUTTON_1)) { + boolean contained = false; + Vector3f mouse = window.getMouseCoords(camera); + for (int i = 0; i < boxes.length; i++) { + if(boxes[i].contains(mouse)) { + boxes[i].setActive(mouse); + focus = i; + contained = true; + } + else + boxes[i].hideCursor(); + } + if(!contained) + focus = -1; + } + if(focus > -1) + boxes[focus].update(); + } + + public void render() { + for (int i = 0; i < boxes.length; i++) + boxes[i].render(); + } + + public ArrayList[] getCommands() { + ArrayList[] commands = (ArrayList[])new ArrayList[4]; + for (int i = 0; i < commands.length; i++) + commands[i] = boxes[i].getCommands(i + 1); + return commands; + } +} diff --git a/src/com/gnarly/game/text/TextBox.java b/src/com/gnarly/game/text/TextBox.java new file mode 100644 index 0000000..2622c08 --- /dev/null +++ b/src/com/gnarly/game/text/TextBox.java @@ -0,0 +1,42 @@ +package com.gnarly.game.text; + +import com.gnarly.engine.display.Camera; +import com.gnarly.engine.rects.ColRect; +import com.gnarly.engine.text.Font; + +public class TextBox { + + private static Font font; + + private Camera camera; + private String text; + + private ColRect back, body; + + private float x, y, depth, width, height, textSize; + + public TextBox(Camera camera, String text, float x, float y, float depth, float width, float height, float textSize) { + this.camera = camera; + this.text = text; + this.x = x; + this.y = y; + this.depth = depth; + this.width = width; + this.height = height; + this.textSize = textSize; + back = new ColRect(camera, x, y, depth, width, height, 1, 1, 1, 1); + body = new ColRect(camera, x + 1, y + 1, depth, width - 2, height - 2, 0, 0, 0, 1); + if(font == null) + font = new Font(camera, "res/img/fonts/default.png"); + } + + public void render() { + font.drawString(text, x + 2, y + 2, depth, textSize, 1, 1, 1, 1); + body.render(); + back.render(); + } + + public void setText(String text) { + this.text = text; + } +} diff --git a/src/com/gnarly/game/text/TextEditor.java b/src/com/gnarly/game/text/TextEditor.java new file mode 100644 index 0000000..c6a370e --- /dev/null +++ b/src/com/gnarly/game/text/TextEditor.java @@ -0,0 +1,313 @@ +package com.gnarly.game.text; + +import java.util.ArrayList; + +import org.joml.Vector3f; + +import com.gnarly.engine.display.Camera; +import com.gnarly.engine.display.Window; +import com.gnarly.engine.rects.ColRect; +import com.gnarly.engine.text.Font; +import com.gnarly.game.commands.Command; +import com.gnarly.game.commands.Fire; +import com.gnarly.game.commands.Rotate; +import com.gnarly.game.commands.Wait; + +public class TextEditor { + + private static final float + HEADER_HEIGHT = 10.666666666f, + TEXT_HEIGHT = 5.333333333f; + + private Window window; + private Camera camera; + private Font font; + private String name; + + private ColRect background; + private ColRect header; + private ColRect body; + + private float x, y, depth, width, height; + + private int maxLength; + private StringBuilder[] contents; + + private ColRect cursor; + private int cx, cy; + private boolean showing; + private float lastTime, blinkRate; + + public TextEditor(Window window, Camera camera, Font font, String name, float x, float y, float depth, float width, float height) { + this.window = window; + this.camera = camera; + this.font = font; + this.name = name; + this.x = x; + this.y = y; + this.depth = depth; + this.width = width; + this.height = height; + background = new ColRect(camera, x, y, depth, width, height, 1, 1, 1, 1); + header = new ColRect(camera, x + 1, y + 1, depth, width - 2, HEADER_HEIGHT, 0, 0, 0, 1); + body = new ColRect(camera, x + 1, y + HEADER_HEIGHT + 2, depth, width - 2, height - HEADER_HEIGHT - 3, 0, 0, 0, 1); + maxLength = (int) ((body.getWidth() - 2) / font.getWidth(TEXT_HEIGHT)); + contents = new StringBuilder[(int) ((body.getHeight() - 1) / (TEXT_HEIGHT + 1))]; + for (int i = 0; i < contents.length; i++) + contents[i] = new StringBuilder(); + cursor = new ColRect(camera, body.getX() + 1, body.getY() + 1, depth, font.getCharWidth(TEXT_HEIGHT), TEXT_HEIGHT, 1, 1, 1, 1); + cx = 0; + cy = 0; + showing = false; + lastTime = System.nanoTime() / 1000000f; + blinkRate = 500; + } + + public void render() { + if(showing) + cursor.render(); + font.drawString(name, x + 2, y + 2, depth, HEADER_HEIGHT - 2, 1, 1, 1, 1); + for (int i = 0; i < contents.length; i++) + font.drawString(contents[i].toString(), body.getX() + 1, body.getY() + i * (TEXT_HEIGHT + 1) + 1, depth, TEXT_HEIGHT, 1, 1, 1, 1); + header.render(); + body.render(); + background.render(); + } + + public void update() { + input(); + cursor(); + } + + private void input() { + StringBuilder in = new StringBuilder(window.getInput().toUpperCase()); + if(in.length() > 0) { + showing = true; + lastTime = System.nanoTime() / 1000000f; + } + for (int i = 0; i < in.length(); i++) { + char c = in.charAt(i); + if(c == 10 && cy < contents.length - 1) { // New line + if(cx == contents[cy].length()) { + ++cy; + cx = 0; + } + else if(contents[contents.length - 1].length() == 0) { + for (int j = contents.length - 1; j > cy + 1; --j) { + contents[j].delete(0, contents[j].length()); + contents[j].append(contents[j - 1]); + } + contents[cy + 1].delete(0, contents[cy + 1].length()); + contents[cy + 1].append(contents[cy].substring(cx, contents[cy].length())); + contents[cy].delete(cx, contents[cy].length()); + ++cy; + cx = 0; + } + } + else if(c == 9) { + --cx; + for (int j = 0; j < 4 && cx + j <= maxLength; j++) + contents[cy].insert(++cx, (char) 32); + } + else if(c == 8) { // Backspace + if(cx > 0) { // Mid-line + contents[cy].deleteCharAt(cx - 1); + --cx; + } + else if(cy > 0) { // Beginning of line + if(contents[cy - 1].length() + contents[cy].length() <= maxLength) { + contents[cy - 1].append(contents[cy]); + for (int j = cy; j < contents.length; j++) { + contents[j].setLength(0); + if(j < contents.length - 1) + contents[j].append(contents[j + 1]); + } + } + --cy; + cx = contents[cy].length(); + } + } + else if(c == 127) { // Delete + if(cx < contents[cy].length()) // Mid-line + contents[cy].deleteCharAt(cx); + else if(cy < contents.length - 1 && contents[cy].length() + contents[cy + 1].length() <= maxLength) { // End of line + contents[cy].append(contents[cy + 1]); + for (int j = cy + 1; j < contents.length; j++) { + contents[j].setLength(0); + if(j < contents.length - 1) + contents[j].append(contents[j + 1]); + } + } + } + else if(c == 1) { // Left arrow + --cx; + if(cx == -1) { + if(cy == 0) + ++cx; + else { + --cy; + cx = contents[cy].length(); + } + } + } + else if(c == 2) { // Right arrow + ++cx; + if(cx > contents[cy].length()) { + if(cy == contents.length - 1) + --cx; + else { + ++cy; + cx = 0; + } + } + } + else if(c == 3 && cy > 0) { // Up arrow + --cy; + if(cx > contents[cy].length()) + cx = contents[cy].length(); + } + else if(c == 4 && cy < contents.length - 1) { // Down arrow + ++cy; + if(cx > contents[cy].length()) + cx = contents[cy].length(); + } + else if((range(c, 48, 57) || range(c, 65, 90) || c == 32) && cx < maxLength) { + contents[cy].insert(cx, c); + ++cx; + } + } + if(contents[cy].length() > maxLength) + contents[cy].setLength(maxLength); + } + + private void cursor() { + if(System.nanoTime() / 1000000f - lastTime > blinkRate) { + showing = !showing; + lastTime += blinkRate; + } + cursor.setPos(body.getX() + cx * font.getWidth(TEXT_HEIGHT) + 1, body.getY() + cy * (TEXT_HEIGHT + 1) + 1); + } + + public boolean contains(Vector3f vector) { + return vector.x >= this.x && vector.y >= this.y && vector.x < this.x + width && vector.y < this.y + height; + } + + public void setActive(Vector3f coords) { + int tempY = clampY((int) ((coords.y - body.getY()) / (TEXT_HEIGHT + 1))); + int tempX = clampX((int) ((coords.x - body.getX()) / font.getWidth(TEXT_HEIGHT))); + if(tempX > -1 && tempY > -1 && tempX <= maxLength && tempY <= contents.length) { + cx = tempX; + cy = tempY + 1; + while(cy > 0 && contents[--cy].length() == 0); + if(cx > contents[cy].length()) + cx = contents[cy].length(); + } + showing = true; + lastTime = System.nanoTime() / 1000000f; + } + + private int clampX(int x) { + if(x < 0) + x = 0; + else if(x > contents[cy].length()) + x = contents[cy].length(); + return x; + } + + private int clampY(int y) { + if(y < 0) + y = 0; + else if(y > contents.length - 1) + y = contents.length - 1; + return y; + } + + public void hideCursor() { + showing = false; + } + + private boolean range(char c, int start, int end) { + return c >= start && c <= end; + } + + public ArrayList getCommands(int thread) { + ArrayList commands = new ArrayList<>(); + for (int i = 0; i < contents.length; i++) { + if(contents[i].length() > 0) { + if(contents[i].length() >= 6 && contents[i].substring(0, 6).equals("ROTATE")) { + int x = 0, y = 0; + String[] split = contents[i].toString().split(" "); + if(split.length == 3) { + for (int j = 0; j < split[1].length(); j++) { + char c = split[1].charAt(j); + if(c >= 48 && c <= 57) { + x *= 10; + x += c - 48; + } + else { + Console.error.add("ROTATE REQUIRES EXACTLY 2 SPACE SEPERATED INTEGERS - THREAD: " + thread + " - LINE " + (i + 1)); + j = contents[i].length(); + } + } + for (int j = 0; j < split[2].length(); j++) { + char c = split[2].charAt(j); + if(c >= 48 && c <= 57) { + y *= 10; + y += c - 48; + } + else { + Console.error.add("ROTATE REQUIRES EXACTLY 2 SPACE SEPERATED INTEGERS - THREAD: " + thread + " - LINE " + (i + 1)); + j = contents[i].length(); + } + } + } + else { + Console.error.add("ROTATE REQUIRES EXACTLY 2 SPACE SEPERATED INTEGERS - THREAD: " + thread + " - LINE " + (i + 1)); + } + if(x > 11 || y > 7) + Console.error.add("CANNOT EXCEED MAP LIMITS OF 12X8 - THREAD: " + thread + " - LINE " + (i + 1)); + else + commands.add(new Rotate(x, y)); + } + else if(contents[i].length() >= 4 && contents[i].substring(0, 4).equals("WAIT")) { + int num = 0; + for (int j = 5; j < contents[i].length(); ++j) { + char c = contents[i].charAt(j); + if(c >= 48 && c <= 57) { + num *= 10; + num += c - 48; + } + else if(c != 32) { + Console.error.add("WAIT CANNOT BE FOLLOWED BY NON-NUMERIC CHARACTERS - THREAD: " + thread + " - LINE " + (i + 1)); + j = contents[i].length(); + } + } + if(num == 0) + num = 1; + commands.add(new Wait(num)); + } + else if(contents[i].length() >= 6 && contents[i].substring(0, 4).equals("FIRE")) { + int num = 0; + for (int j = 5; j < contents[i].length(); ++j) { + char c = contents[i].charAt(j); + if(c >= 48 && c <= 57) { + num *= 10; + num += c - 48; + } + else if(c != 32) { + Console.error.add("FIRE MUST BE FOLLWED BY NUMBER - THREAD: " + thread + " - LINE: " + (i + 1)); + j = contents[i].length(); + } + } + if(num > 99) + Console.error.add("NUMBER " + num + " IS NOT A VALID NUMBER FOR FIRE - THREAD: " + thread + " - LINE: " + (i + 1)); + else + commands.add(new Fire(num)); + } + else + Console.error.add("UNRECOGNIZED COMMAND: " + (contents[i].toString().length() > 25 ? (contents[i].toString().substring(0, 23) + "...") : contents[i].toString()) + " - THREAD: " + thread + " - LINE: " + (i + 1)); + } + } + return commands; + } +}