From ccbbb0a5b01e2c4903e61d2f4e6d8ca41632fb68 Mon Sep 17 00:00:00 2001 From: Gnarly Narwhal Date: Sun, 10 Mar 2019 22:34:10 -0700 Subject: [PATCH] Entire project --- .gitignore | 12 + LICENSE | 21 ++ README.md | 22 ++ res/img/bars/blcorner.png | Bin 0 -> 194 bytes res/img/bars/brcorner.png | Bin 0 -> 197 bytes res/img/bars/corner.png | Bin 0 -> 188 bytes res/img/bars/leftbar.png | Bin 0 -> 271 bytes res/img/bars/numbers/0.png | Bin 0 -> 174 bytes res/img/bars/numbers/1.png | Bin 0 -> 171 bytes res/img/bars/numbers/10.png | Bin 0 -> 181 bytes res/img/bars/numbers/11.png | Bin 0 -> 158 bytes res/img/bars/numbers/12.png | Bin 0 -> 190 bytes res/img/bars/numbers/2.png | Bin 0 -> 185 bytes res/img/bars/numbers/3.png | Bin 0 -> 185 bytes res/img/bars/numbers/4.png | Bin 0 -> 174 bytes res/img/bars/numbers/5.png | Bin 0 -> 183 bytes res/img/bars/numbers/6.png | Bin 0 -> 182 bytes res/img/bars/numbers/7.png | Bin 0 -> 178 bytes res/img/bars/numbers/8.png | Bin 0 -> 180 bytes res/img/bars/numbers/9.png | Bin 0 -> 181 bytes res/img/bars/tlcorner.png | Bin 0 -> 188 bytes res/img/bars/topbar.png | Bin 0 -> 285 bytes res/img/bars/trcorner.png | Bin 0 -> 196 bytes res/img/fonts/default.png | Bin 0 -> 1160 bytes res/img/header.png | Bin 0 -> 850 bytes res/img/map/buttons/menu/hovered.png | Bin 0 -> 350 bytes res/img/map/buttons/menu/pressed.png | Bin 0 -> 349 bytes res/img/map/buttons/menu/unpressed.png | Bin 0 -> 366 bytes res/img/map/buttons/run/hovered.png | Bin 0 -> 303 bytes res/img/map/buttons/run/pressed.png | Bin 0 -> 337 bytes res/img/map/buttons/run/unpressed.png | Bin 0 -> 307 bytes res/img/map/buttons/smenu/hovered.png | Bin 0 -> 227 bytes res/img/map/buttons/smenu/pressed.png | Bin 0 -> 225 bytes res/img/map/buttons/smenu/unpressed.png | Bin 0 -> 253 bytes res/img/map/buttons/stop/hovered.png | Bin 0 -> 249 bytes res/img/map/buttons/stop/pressed.png | Bin 0 -> 250 bytes res/img/map/buttons/stop/unpressed.png | Bin 0 -> 291 bytes res/img/map/cannon.png | Bin 0 -> 994 bytes res/img/map/empty.png | Bin 0 -> 350 bytes res/img/map/laser/laserBend.png | Bin 0 -> 1219 bytes res/img/map/laser/laserBendBack.png | Bin 0 -> 1204 bytes res/img/map/laser/laserStraight.png | Bin 0 -> 1304 bytes res/img/map/laser/laserStraightBack.png | Bin 0 -> 1226 bytes res/img/map/mirror135.png | Bin 0 -> 415 bytes res/img/map/mirror45.png | Bin 0 -> 417 bytes res/img/map/track.png | Bin 0 -> 460 bytes res/img/map/trigger.png | Bin 0 -> 2142 bytes res/img/menu/background.png | Bin 0 -> 3887 bytes res/img/menu/close/hovered.png | Bin 0 -> 230 bytes res/img/menu/close/pressed.png | Bin 0 -> 228 bytes res/img/menu/close/unpressed.png | Bin 0 -> 242 bytes res/img/menu/help/helpMenu.png | Bin 0 -> 1577 bytes res/img/menu/help/hovered.png | Bin 0 -> 683 bytes res/img/menu/help/pressed.png | Bin 0 -> 681 bytes res/img/menu/help/unpressed.png | Bin 0 -> 641 bytes res/img/menu/next/hovered.png | Bin 0 -> 232 bytes res/img/menu/next/pressed.png | Bin 0 -> 230 bytes res/img/menu/next/unpressed.png | Bin 0 -> 245 bytes res/img/menu/play/hovered.png | Bin 0 -> 734 bytes res/img/menu/play/pressed.png | Bin 0 -> 733 bytes res/img/menu/play/unpressed.png | Bin 0 -> 686 bytes res/img/menu/prev/hovered.png | Bin 0 -> 229 bytes res/img/menu/prev/pressed.png | Bin 0 -> 227 bytes res/img/menu/prev/unpressed.png | Bin 0 -> 245 bytes res/maps/level1.llm | 13 + res/maps/level2.llm | 13 + res/maps/level3.llm | 13 + res/samby.png | Bin 0 -> 241 bytes res/sambyanim.png | Bin 0 -> 917 bytes res/shaders/s2a/frag.fs | 13 + res/shaders/s2a/vert.vs | 17 + res/shaders/s2c/frag.fs | 9 + res/shaders/s2c/vert.vs | 11 + res/shaders/s2t/frag.fs | 13 + res/shaders/s2t/vert.vs | 15 + res/shaders/s2x/frag.fs | 15 + res/shaders/s2x/vert.vs | 17 + src/com/gnarly/engine/display/Camera.java | 65 ++++ .../gnarly/engine/display/Framebuffer.java | 65 ++++ src/com/gnarly/engine/display/Window.java | 279 ++++++++++++++++ src/com/gnarly/engine/model/Vao.java | 68 ++++ src/com/gnarly/engine/rects/ColRect.java | 42 +++ src/com/gnarly/engine/rects/Rect.java | 84 +++++ src/com/gnarly/engine/rects/TexRect.java | 94 ++++++ src/com/gnarly/engine/shaders/Shader.java | 131 ++++++++ src/com/gnarly/engine/shaders/Shader2a.java | 24 ++ src/com/gnarly/engine/shaders/Shader2c.java | 22 ++ src/com/gnarly/engine/shaders/Shader2t.java | 8 + src/com/gnarly/engine/shaders/Shader2x.java | 30 ++ src/com/gnarly/engine/text/Font.java | 82 +++++ src/com/gnarly/engine/texture/Anim.java | 57 ++++ src/com/gnarly/engine/texture/Texture.java | 83 +++++ src/com/gnarly/game/GamePanel.java | 116 +++++++ src/com/gnarly/game/Main.java | 90 +++++ src/com/gnarly/game/MenuPanel.java | 104 ++++++ src/com/gnarly/game/board/Map.java | 308 +++++++++++++++++ src/com/gnarly/game/board/laser/Cannon.java | 27 ++ src/com/gnarly/game/board/laser/Laser.java | 182 ++++++++++ src/com/gnarly/game/board/laser/Trigger.java | 79 +++++ src/com/gnarly/game/commands/Command.java | 5 + src/com/gnarly/game/commands/Fire.java | 18 + src/com/gnarly/game/commands/Rotate.java | 23 ++ src/com/gnarly/game/commands/Wait.java | 22 ++ src/com/gnarly/game/objs/Button.java | 77 +++++ src/com/gnarly/game/text/Console.java | 79 +++++ src/com/gnarly/game/text/EditorManager.java | 85 +++++ src/com/gnarly/game/text/TextBox.java | 42 +++ src/com/gnarly/game/text/TextEditor.java | 313 ++++++++++++++++++ 108 files changed, 2908 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 res/img/bars/blcorner.png create mode 100644 res/img/bars/brcorner.png create mode 100644 res/img/bars/corner.png create mode 100644 res/img/bars/leftbar.png create mode 100644 res/img/bars/numbers/0.png create mode 100644 res/img/bars/numbers/1.png create mode 100644 res/img/bars/numbers/10.png create mode 100644 res/img/bars/numbers/11.png create mode 100644 res/img/bars/numbers/12.png create mode 100644 res/img/bars/numbers/2.png create mode 100644 res/img/bars/numbers/3.png create mode 100644 res/img/bars/numbers/4.png create mode 100644 res/img/bars/numbers/5.png create mode 100644 res/img/bars/numbers/6.png create mode 100644 res/img/bars/numbers/7.png create mode 100644 res/img/bars/numbers/8.png create mode 100644 res/img/bars/numbers/9.png create mode 100644 res/img/bars/tlcorner.png create mode 100644 res/img/bars/topbar.png create mode 100644 res/img/bars/trcorner.png create mode 100644 res/img/fonts/default.png create mode 100644 res/img/header.png create mode 100644 res/img/map/buttons/menu/hovered.png create mode 100644 res/img/map/buttons/menu/pressed.png create mode 100644 res/img/map/buttons/menu/unpressed.png create mode 100644 res/img/map/buttons/run/hovered.png create mode 100644 res/img/map/buttons/run/pressed.png create mode 100644 res/img/map/buttons/run/unpressed.png create mode 100644 res/img/map/buttons/smenu/hovered.png create mode 100644 res/img/map/buttons/smenu/pressed.png create mode 100644 res/img/map/buttons/smenu/unpressed.png create mode 100644 res/img/map/buttons/stop/hovered.png create mode 100644 res/img/map/buttons/stop/pressed.png create mode 100644 res/img/map/buttons/stop/unpressed.png create mode 100644 res/img/map/cannon.png create mode 100644 res/img/map/empty.png create mode 100644 res/img/map/laser/laserBend.png create mode 100644 res/img/map/laser/laserBendBack.png create mode 100644 res/img/map/laser/laserStraight.png create mode 100644 res/img/map/laser/laserStraightBack.png create mode 100644 res/img/map/mirror135.png create mode 100644 res/img/map/mirror45.png create mode 100644 res/img/map/track.png create mode 100644 res/img/map/trigger.png create mode 100644 res/img/menu/background.png create mode 100644 res/img/menu/close/hovered.png create mode 100644 res/img/menu/close/pressed.png create mode 100644 res/img/menu/close/unpressed.png create mode 100644 res/img/menu/help/helpMenu.png create mode 100644 res/img/menu/help/hovered.png create mode 100644 res/img/menu/help/pressed.png create mode 100644 res/img/menu/help/unpressed.png create mode 100644 res/img/menu/next/hovered.png create mode 100644 res/img/menu/next/pressed.png create mode 100644 res/img/menu/next/unpressed.png create mode 100644 res/img/menu/play/hovered.png create mode 100644 res/img/menu/play/pressed.png create mode 100644 res/img/menu/play/unpressed.png create mode 100644 res/img/menu/prev/hovered.png create mode 100644 res/img/menu/prev/pressed.png create mode 100644 res/img/menu/prev/unpressed.png create mode 100644 res/maps/level1.llm create mode 100644 res/maps/level2.llm create mode 100644 res/maps/level3.llm create mode 100644 res/samby.png create mode 100644 res/sambyanim.png create mode 100644 res/shaders/s2a/frag.fs create mode 100644 res/shaders/s2a/vert.vs create mode 100644 res/shaders/s2c/frag.fs create mode 100644 res/shaders/s2c/vert.vs create mode 100644 res/shaders/s2t/frag.fs create mode 100644 res/shaders/s2t/vert.vs create mode 100644 res/shaders/s2x/frag.fs create mode 100644 res/shaders/s2x/vert.vs create mode 100644 src/com/gnarly/engine/display/Camera.java create mode 100644 src/com/gnarly/engine/display/Framebuffer.java create mode 100644 src/com/gnarly/engine/display/Window.java create mode 100644 src/com/gnarly/engine/model/Vao.java create mode 100644 src/com/gnarly/engine/rects/ColRect.java create mode 100644 src/com/gnarly/engine/rects/Rect.java create mode 100644 src/com/gnarly/engine/rects/TexRect.java create mode 100644 src/com/gnarly/engine/shaders/Shader.java create mode 100644 src/com/gnarly/engine/shaders/Shader2a.java create mode 100644 src/com/gnarly/engine/shaders/Shader2c.java create mode 100644 src/com/gnarly/engine/shaders/Shader2t.java create mode 100644 src/com/gnarly/engine/shaders/Shader2x.java create mode 100644 src/com/gnarly/engine/text/Font.java create mode 100644 src/com/gnarly/engine/texture/Anim.java create mode 100644 src/com/gnarly/engine/texture/Texture.java create mode 100644 src/com/gnarly/game/GamePanel.java create mode 100644 src/com/gnarly/game/Main.java create mode 100644 src/com/gnarly/game/MenuPanel.java create mode 100644 src/com/gnarly/game/board/Map.java create mode 100644 src/com/gnarly/game/board/laser/Cannon.java create mode 100644 src/com/gnarly/game/board/laser/Laser.java create mode 100644 src/com/gnarly/game/board/laser/Trigger.java create mode 100644 src/com/gnarly/game/commands/Command.java create mode 100644 src/com/gnarly/game/commands/Fire.java create mode 100644 src/com/gnarly/game/commands/Rotate.java create mode 100644 src/com/gnarly/game/commands/Wait.java create mode 100644 src/com/gnarly/game/objs/Button.java create mode 100644 src/com/gnarly/game/text/Console.java create mode 100644 src/com/gnarly/game/text/EditorManager.java create mode 100644 src/com/gnarly/game/text/TextBox.java create mode 100644 src/com/gnarly/game/text/TextEditor.java 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 0000000000000000000000000000000000000000..b6836fabf23d8ba79ce98effe2c919441e78b189 GIT binary patch literal 194 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1SBVD?P>#3Ea{HEjtmSN`?>!lvI6;>1s;*b zKs5(Im@(^GCnr!)vcxr_Bsf2kvzDx!&G!dVupEO4mXZ im=YJ*HF^9~V)(!E#YW?F;oU%^7(8A5T-G@yGywq3jy6vK literal 0 HcmV?d00001 diff --git a/res/img/bars/brcorner.png b/res/img/bars/brcorner.png new file mode 100644 index 0000000000000000000000000000000000000000..a353efb673c30c34c2a43b0376ffdd930189f006 GIT binary patch literal 197 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1SBVD?P>#3Ea{HEjtmSN`?>!lvI6;>1s;*b zKs5(Im@(^GCnr!)vcxr_Bsf2`DHH;e!P literal 0 HcmV?d00001 diff --git a/res/img/bars/corner.png b/res/img/bars/corner.png new file mode 100644 index 0000000000000000000000000000000000000000..3cabf85db0bba72929ab3edc6e65320c03c3b6cc GIT binary patch literal 188 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1SBVD?P>#3Ea{HEjtmSN`?>!lvI6;>1s;*b zKsARzm{C;2s{tq|S>hT|5}cn_Ql40p%21G)nOCBhms+A=qGzCIXy%f+{wq+CrKgKy zh(>U7ht4m56(-Hp0A)VMsi)#y1YO+jb_FSLM>0tsT-sqO+99y)k&{5_ bCvnD%8*lZx@~+1K4Po$f^>bP0l+XkKAWt(e literal 0 HcmV?d00001 diff --git a/res/img/bars/leftbar.png b/res/img/bars/leftbar.png new file mode 100644 index 0000000000000000000000000000000000000000..346aff826ec13f6fa431b5e38c09e7a4cf830212 GIT binary patch literal 271 zcmeAS@N?(olHy`uVBq!ia0vp^d_b(g!2~4N_S`!Fq*&4&eH|GXHuiJ>Nn{1`ISV`@ zi-GD6f-vKbiP>*~f|4b!5hcO-X(i=}MX3x0iJ5sNdU>fO3MP66dWL2$nd`p-74>+! zIEGmGzrA#jx50peHK443;qlSAF^2{3Y+$zVR=vccA+o5`d~*GUKjC^}+x8xcO4Kuu zHCwAH5Kw0Lw9@D4g#Eg2)tsg7PMKS3S31{S^H`$IE6Xzp*#gT>ujGEPt^p0E0S9z>_6}0Nr6t9WFKC3khk8YTEnl<!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK3b!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK3b zV~9p@@{j-j?PZKTa)0q3(nu;`3z<~In!xu~8#taN=D;MZ8XyrNp^)Pt4 L`njxgN@xNAW*ROv literal 0 HcmV?d00001 diff --git a/res/img/bars/numbers/10.png b/res/img/bars/numbers/10.png new file mode 100644 index 0000000000000000000000000000000000000000..716d690c0e29851eca7ab7b3f4530aebfcfc9c61 GIT binary patch literal 181 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqEa{HEjtmSN`?>!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK3b9gLU*uaF8yH&>Yi3AvF{OA~v>8qiU7&GH&4Zm)L*f{NrbGCb|NraR7|xZn VK5Uh-zYNsP;OXk;vd$@?2>@1`GfMyf literal 0 HcmV?d00001 diff --git a/res/img/bars/numbers/11.png b/res/img/bars/numbers/11.png new file mode 100644 index 0000000000000000000000000000000000000000..5757453dd45dda9ad218966df2f59b810dee9940 GIT binary patch literal 158 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqEa{HEjtmSN`?>!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK3bs(leGA=drQ-Q9)<@}GZ}eRZiE7rF?hQAxvX!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK3b9gLU*uaF8yH&>Yi3AvF{OA~v>8qiU7+D6=5nk;^nmI{O^0bLp93vCCKd1` eCxmZ!Y0toYyug# literal 0 HcmV?d00001 diff --git a/res/img/bars/numbers/2.png b/res/img/bars/numbers/2.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d956da2c0b9914771743c2eba3f1ceb50937b5 GIT binary patch literal 185 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqEa{HEjtmSN`?>!lvI6;>1s;*b z3=Dh+L6~vJ#O${~LCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33Jwr2>%=KS^ip)G+ z978mMlYjjGZ!cl&k^8H^=i#FpDv1)#j2*q_*tr#wXQ+g+zL=C?STiZ1u*5UPr={sX a55sH!so70_y^nw!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK3bO6;0o7{h4hD)8|? a55tL%)8<-oN9_cfz~JfX=d#Wzp$P!5t2Xlh literal 0 HcmV?d00001 diff --git a/res/img/bars/numbers/4.png b/res/img/bars/numbers/4.png new file mode 100644 index 0000000000000000000000000000000000000000..a0b993408500cccc872a96e07bb13ed5ec2d648f GIT binary patch literal 174 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqEa{HEjtmSN`?>!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK3b!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK3bZA!u6{1-oD!M<8tyfo literal 0 HcmV?d00001 diff --git a/res/img/bars/numbers/6.png b/res/img/bars/numbers/6.png new file mode 100644 index 0000000000000000000000000000000000000000..9fb751d321776104e5fb9f6ea6d765a60966bdc9 GIT binary patch literal 182 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqEa{HEjtmSN`?>!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK3b?Oo{d2^ Wdy3@=i!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK3b!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK3b!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK3b#3Ea{HEjtmSN`?>!lvI6;>1s;*b zKs5(Jm~qF%?6*Kc$r9IylHmNblJdl&REC1Y%)Ao4ywnl}6FmbxLo=7m^$nk5nHwJS)0JCaHA;L;9L(GG!SkDLTb bKZ!GD+<2?km3KV`Xb6L+tDnm{r-UW|;Kek^ literal 0 HcmV?d00001 diff --git a/res/img/bars/topbar.png b/res/img/bars/topbar.png new file mode 100644 index 0000000000000000000000000000000000000000..a139be143c28c7768801fa49993cb9bd44ee264f GIT binary patch literal 285 zcmeAS@N?(olHy`uVBq!ia0vp^3P8-q!2~1^?(2LCq*&4&eH|GXHuiJ>Nn{1`ISV`@ zi-Bqmf-vKbiP>*~f|4b!5hcO-X(i=}MX3x0iJ5sNdU>fO3MP66dWL2$nd`p-70vQ= zaSXBW-#g_XZ?k~_%Ux$Khnh>lw-&HiEn&z~Y(F3M_-p0oq9qd_?6^DekHO2$vB91% zYtJsVnWlU!>~*A(?Y>1^AI{Wr@$db{z@NEd(#l(DFN&@zE#Ts8_V-T^+#(vrE6%6W z^aPe2zNXyu=(+3Z0P#N$!xP#3Ea{HEjtmSN`?>!lvI6;>1s;*b zKs5(Jm~qF%?6*Kc$r9IylHmNblJdl&REC1Y%)Ao4ywnl}6FmbxLo=7m^ZEX{uuNo{q_L<% kz)5pLE4QP_iFg(U!JjV`H~bd44>XLy)78&qol`;+08i{W+W-In literal 0 HcmV?d00001 diff --git a/res/img/fonts/default.png b/res/img/fonts/default.png new file mode 100644 index 0000000000000000000000000000000000000000..a3dc7114d9b2d76b29cf0a086e2ea4ed5ed0b42b GIT binary patch literal 1160 zcmeAS@N?(olHy`uVBq!ia0vp^0YKcq!3HGf=)Y?MQY`6?zK#qG8~eHcB(eheoCO|{ z#S9F5`$3q|f;CtLC@5Lt8c`CQpH@&V% z7srr_xVO_U<|$hWFn|AFZ?(PT4%^wqZd=rEvh#Cmc*p8KKNulXP`_{AzE_;NYj&Dl zi;>P=*K2zH*sSd2vs*VlyA@H4Rk)(OVE3=Jy^kN1eVzS}+eYyPf5r8O_j(_cU1zo4 zarY|g_T74~;y%o%T*#MKF3O(UKU4hGgR=0#2Q$C-%TIX7?(jVE`j?P9<+AVYuKLS8 z^EQ9Q?g?EB2dZCBrg&*fhHS@7L%od>@Qt}J_P zBeS($b;sS(g-rX5*R8*|bj?4xbA_z?AC~Vv7JJeB*S8PsEbpG!RxJ0uztwT}?$w9a z`Cc!6V4mFX8I3yGcXPA(uHP-cYkk1F?zGmO zpR(q5jPKhsx1VF)eQ57Pv)J?lxxWIF`|f?{ldX6*nYD_kmU*5Ozs?28KOwXCWjAlT zTbs5a`|?+oUu-s}auExRf2=gCz0Q`q`}diI>&sqqe&Maivf1E}e)R2~+gHCI{KGqK zX64rOb2Z{0emm|zZT;bgyFXJ39h=Rm2p9mDa5bB{5V&zD?t>s^fL-Isq?^n!$^Wd6DEuY@qG6Q{Zk?#d(+WFm!t+`{`6*9d)GXC5C;6#g$IuTps-LzEvUE_*y^nu`A2E{Y!eq1$X?LWQJ1$nBeU_7Z^V%a|6gTe~DWM4fxgty1 literal 0 HcmV?d00001 diff --git a/res/img/header.png b/res/img/header.png new file mode 100644 index 0000000000000000000000000000000000000000..f590b34ec859d8dda21d8ec8eef64e60e254f65f GIT binary patch literal 850 zcmeAS@N?(olHy`uVBq!ia0vp^Pk>m4gAGU)P80qKq!^2X+?^QKos)S9a~60+7BevL9R^{>T!q0}hr8JO3{=ws2?{I3;$Y@cW`K_w-Aj95Uwq zdB3^4`}KL9{|ejZcKFTGn|*!5-@Df;zjdY0EDujBGN0VCs!oM7^lz`q#n5G^pPA;= zU*EamUhK@1cYTCb{hFu}^3{BjOZZH%zNN*H$=@^Hz4$pV|IEBf^}xhkIdO--O+V+_ zdw229#m}qv8CE}=>(csiQzh^1(C3q$y}P%@-dSbgDtS+*Q2R+LyH1td+|%}URdL|n z*`9SPsz8P-d#-q8?YT(Xclz1v8+B)o@0b{~>C*ds#!j2l?|E)X23v5?b?vUXKhJvY z|9y_*EYRMIeLx#}pTC>FMd|0V;&{m)ZtvcmJ(m6=KV@n2f}2yzpPjpR=H6yyyKaw5wcuE>Uti3*2N-G{=P%s%c(>+^ z#g*8~_Hv_>{6BBTnSa((^85~oqB-YSKjgV?dh_i5&pkn<=gz#F>wa&g$F#ez*1U2D z+IJOVUx}XkRoBly@6kPFfrItV4bNTF^W)D!zwFE4;`flt3 zP7ik>L9(+(Qt9NT-9`6arrmRdMDdE?xxXcqz~T1JJl1S|;%}dQ94;ow@>Aa%U+wN( rao$ere&>rNpwywKw{HIix&HtB=3+C7{;^F5CV2)=S3j3^P6 zx;TbJxWApi$kl8h!1DM1`cFo$#kNg(%ES5M8B@5#kt3T9%dFkHS>mzu>tC|2^SewP zUqyV7a{T{Y?1q=D_jwLy%ex!5hU)M>DLEdtE8@#Vjfu-I%SK;2C@9euXKTOQdRx%3 zu6crfb<*{6S6aPY`a%Ph>K=3y}7sW{)gC?cWrsMO*^`)+3r_Q z!LH=fRu)V(9QP}iTio3^)hO%>RE&8~$2~!yp_h&+O_V=(=icq3QG%U0!r7@Rn|yU_ oqYsL0+!U};?Z;6qw~XiPdP4r;{XZo)0KLiJ>FVdQ&MBb@0CrZ5ZvX%Q literal 0 HcmV?d00001 diff --git a/res/img/map/buttons/menu/pressed.png b/res/img/map/buttons/menu/pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..03ca610a0109f829a6b252e0d17bc0b8816179d2 GIT binary patch literal 349 zcmeAS@N?(olHy`uVBq!ia0vp^-astD!3HEZg)nReQY`6?zK#qG8~eHcB(eheoCO|{ z#S9F52SJ!|$HeTnKtah8*NBqf{Irtt#G+J&g2c?c61}|C5(N`I13g1Cm(2BFfr@T< zx;TbJxWApi$kl8hz*1XPmv{Hjv`upq8SlnC;A)-`Q8r`o)-ti=n&jC1i(dq9RP&JK zj#GZ|{~lBH%H)@li5knTw{7cM+BoOk4KML-J1NyqCA*SOTUjt2zOefF^OECXry?#I zA9~rR`}%?XjlItMAMU=iyILwow|8l}py=yGNxlttt-na`)84y#`N3l(}ch@%{D|1g|VsXFX&QnVE4O9oF@KbLh*2~7ao5R20Q literal 0 HcmV?d00001 diff --git a/res/img/map/buttons/menu/unpressed.png b/res/img/map/buttons/menu/unpressed.png new file mode 100644 index 0000000000000000000000000000000000000000..c05182915d4bfcae9e21627e5f6f3d97e8c3a165 GIT binary patch literal 366 zcmeAS@N?(olHy`uVBq!ia0vp^-astD!3HEZg)nReQY`6?zK#qG8~eHcB(eheoCO|{ z#S9F52SJ!|$HeTnKtah8*NBqf{Irtt#G+J&g2c?c61}|C5(N`I13g1Cm(2BFfr?&x zx;TbJxWAonu**eJfcg9X`d_-PN+v=ndb+h67EChyrT6)}ZC%>m$SR#p*xmyY`vZz@qE|&=;>VFMYLb<*&pe2%Q^%o zzH;(}Z+xQfZqK`M|H}JS5Vq5;a>chCnePK0?$cdTD}JdDLy4x-E0raCom;EBrk38j zucP=DXl-!I6;F@qcCPOx+ArlGBCT5v?kita;4k_rf5rFhK@SEpPK*x literal 0 HcmV?d00001 diff --git a/res/img/map/buttons/run/hovered.png b/res/img/map/buttons/run/hovered.png new file mode 100644 index 0000000000000000000000000000000000000000..b066c4aa633bcd6a5bfc339a4a051b9e40a8ced4 GIT binary patch literal 303 zcmeAS@N?(olHy`uVBq!ia0vp^-astD!3HEZg)nReQY`6?zK#qG8~eHcB(eheoCO|{ z#S9F5`$3q|f;CtLC@5Lt8c`CQpH@qJ8lbxwJYD@<);T3K0RRt+~G?zO=0?XV#xC(Vn^Rcg%s~sz)5FF8p2G z*zVop>~f*lt?{QGBi{#B+xPhf*9~_Z693;c@4NYo*Ag)gyWL^Z7nE1nu4R+;;J$Nqk#6ho}<3ma3I-!@xq_Rzo!Ww!+A9POrvO!io YulYdFN2V(xK%X&qy85}Sb4q9e0BEj$od5s; literal 0 HcmV?d00001 diff --git a/res/img/map/buttons/run/unpressed.png b/res/img/map/buttons/run/unpressed.png new file mode 100644 index 0000000000000000000000000000000000000000..f2c514170733a548bb9fd8bba69008937b95a80f GIT binary patch literal 307 zcmeAS@N?(olHy`uVBq!ia0vp^-astD!3HEZg)nReQY`6?zK#qG8~eHcB(eheoCO|{ z#S9F5`$3q|f;CtLC@5Lt8c`CQpH@v>h6Mp!9a|1}r&KR^dtm+5w(bAdtaX)NB33&g>WgF6 ztlD?CyA({%7QQ><1*C4?xhd+9HEZ=wYh$3?#hts2fxO+t`6@u(^5T3|Aa8qdejR7f zpc76ZbkcZ(0p}9b}GkCiCxvXMtf=i literal 0 HcmV?d00001 diff --git a/res/img/map/buttons/smenu/hovered.png b/res/img/map/buttons/smenu/hovered.png new file mode 100644 index 0000000000000000000000000000000000000000..00f85fad7461f9b2f8a67f30e4aee4780ae295a8 GIT binary patch literal 227 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucLCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33 zJwr2>%=KS^ih?~|978Pp@Ai0dF(~jb|NX!I*QTu27$@<0#LT=By}Z;C1rt33 zJwr2>%=KS^iUK`d978Pp@Ai0dF(~jb*H+cXt)1ZgLP<}&)p63c%PubdGnzi$;(O0> z&Gkc^#si)m{;V}ec2DzW+@bP0l+XkK5G6@1 literal 0 HcmV?d00001 diff --git a/res/img/map/buttons/smenu/unpressed.png b/res/img/map/buttons/smenu/unpressed.png new file mode 100644 index 0000000000000000000000000000000000000000..c571b5b91be8fcfd3d62b9245344eb15fdc3e317 GIT binary patch literal 253 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucLCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33 zJwr2>%=KS^igG<&978Ppm-Ym59WdZ=D*x|Y-f|>i&KqUXo*z23=aznOd~&Q(%lurh zWt)6&dBvFvhSwfQNE$UyDKDQO8B|%$K2vcj3R8e5$Zl8j8v&y)Hw|AgIOlOcUwOyE g|LL*HZ&uG)Z%vLlm+*9FGtgoNPgg&ebxsLQ0N65EN&o-= literal 0 HcmV?d00001 diff --git a/res/img/map/buttons/stop/hovered.png b/res/img/map/buttons/stop/hovered.png new file mode 100644 index 0000000000000000000000000000000000000000..d21c74ba9e523524b5fa590711fa1a48accaa52c GIT binary patch literal 249 zcmeAS@N?(olHy`uVBq!ia0vp^K0qwN!3HD)I;2klDVB6cUq=Rpjs4tz5?O(K&H|6f zVg?4j{UFR}!5XXr6qGD+jVKAuPb(=;EJ|f4NX*PD(aTFMQ83Xn&@(i1$z1;xsHn`- z#W5tp{p|%qt_BBzqXF;lziYhSk)WEPBcgaTdDqVgseOjU_vLbmiq|Ft9bD4nIiLOZ zw)qynzp`!PPTLS`(EPSZGDrMO!tMmN+pNYn^ky6^JII46b)W6`>?Tjv$y{o(2a7Jx Z&^CVCKIOJ@Q#H^944$rjF6*2UngFl)R_y=) literal 0 HcmV?d00001 diff --git a/res/img/map/buttons/stop/pressed.png b/res/img/map/buttons/stop/pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..72f898434df3dcbd279779cf37eb9e40dd55943f GIT binary patch literal 250 zcmeAS@N?(olHy`uVBq!ia0vp^K0qwN!3HD)I;2klDVB6cUq=Rpjs4tz5?O(K&H|6f zVg?4j{UFR}!5XXr6qGD+jVKAuPb(=;EJ|f4NX*PD(aTFMQ83Xn&@(i1$z1;xsHoi2 z#W5tp{p|%qt_BBzqXF+Xe7AosCAfyAw^eCLg^#)XrZa|V?`}8y=B*R;(V7ur@bYuQ z?Qg>A`|lknJIJ$*J8eU(LG#-t$sF-B3A+>6ZnGNS(3^1(L+bbKg!*8El?g!ymncW% bNfonp7ymDFJ7Vb!bOM8?tDnm{r-UW|#7|a? literal 0 HcmV?d00001 diff --git a/res/img/map/buttons/stop/unpressed.png b/res/img/map/buttons/stop/unpressed.png new file mode 100644 index 0000000000000000000000000000000000000000..16c57548c91a86f4498e698280b3b2d22becaf13 GIT binary patch literal 291 zcmeAS@N?(olHy`uVBq!ia0vp^K0qwN!3HD)I;2klDVB6cUq=Rpjs4tz5?O(K&H|6f zVg?4j{UFR}!5XXr6qGD+jVKAuPb(=;EJ|f4NX*PD(aTFMQ83Xn&@(i1$z1;xsAz$w zi(^QH``ZbIe9VkIhkpI{j}LV;U$TW~LqkgS=?T|2|K0lju$`>AfOVci*&T;BI~s4W zy}PoOMd0>j!(9F!lRmy%U}+|UNPmBG{1&t;ucLK6Ub0BVE) literal 0 HcmV?d00001 diff --git a/res/img/map/cannon.png b/res/img/map/cannon.png new file mode 100644 index 0000000000000000000000000000000000000000..9c1e93db9d16a32dd9d50b9cc06504280a1cae3b GIT binary patch literal 994 zcmV<810DQ{P)~S^|00004XF*Lt006O% z3;baP00009a7bBm000iU000iU0TsVC(f|Me8FWQhbW?9;ba!ELWdLwtX>N2bZe?^J zG%heMF*ZbLuk`=`17Ar*K~#8N?VLSM98nO2H^t$K1j#ob@hQkQaDqfc;?y^AzIX7M z_D*);LcQghnKyroW$hID1oQeeU-5#TWmbs8>+AQ4JU)Io9`C{7F!j5hpTAk9LHupv zZ?jMTTPwb_IiG*R{_yY_+}(Ww$grMXPN$dn8^qrx{x=+>^)tdcmG%CufByaW-v<~a!VOY(!0DP2psG5677O*` z0F2nq4>_l6JAXxN=Z72-oBA1{or1y(gcWqJT8RUJT!AbwuN-%I?{ z{r(J$(h$s`-L?u<(H6g=D%#>#R7G3-imGVq8DPCJC>mf}Mb{R;5`X+3>%R^#r3|PJ z* z6%I2#sF6a>{9~HfBtYRekK0+bxGFv#|Id8NGqf4_(0K`X@io%?4&B))T*BJ@FgXb4&jB0Y-^% zqZgAB>jA2&188vwGZX6ps*2xwfU4rR9-yil@K1ULHYu?lrBrnQEp{<8u^uIUU_DCw zzE@V{B3T)|Mr#_zHCSQZQ^eee~Z)0Zw?!ux#;{& QhX4Qo07*qoM6N<$f}l~^%m4rY literal 0 HcmV?d00001 diff --git a/res/img/map/empty.png b/res/img/map/empty.png new file mode 100644 index 0000000000000000000000000000000000000000..25562459d8352aaebbef3e1b5b8ec36efab044c9 GIT binary patch literal 350 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={WSkfJR9T^xl_H+M9WCij$3p^r= z85sEXgD|57Yp@DXP_o1|q9iy!t)x7$D3zfgF*C13FE6!3!9>qM&(O>zbNyGKqT8M> zjv*f2Z>KqOH5&-HR$GZEtV( zr&h`^?p|{9`}9ZKIG!dBmvCbVYv^32UpFTmxNB69t~6iQj48sUFzCTKiB-S8an2Fh pVayf!@xKE|M8$$>$CsqI`^@X}w{p30%>sIp!PC{xWt~$(69B^VgLnV{ literal 0 HcmV?d00001 diff --git a/res/img/map/laser/laserBend.png b/res/img/map/laser/laserBend.png new file mode 100644 index 0000000000000000000000000000000000000000..69d0864f0566c048da28789047b21b2466e0a530 GIT binary patch literal 1219 zcmeAS@N?(olHy`uVBq!ia0y~yU;#226gb#`WY0gYWk8B0-O<;Pfnj4m_n$;oAfL0q zBeIx*f$ty)Gwzs}{T3)FS>hT|5}cn_Ql40p%21G)nOCBhms+A=qGzCIXy%f+{wvTR z2TvErkczms*B$eeEd^K({QSRsZ|aS`53-8y#rN0ej~^LZI~Ds zK6IP>WvH3;_te3{`%5$J<$z{M07a@HB2OW@KCA``RY8P~);)aywdViDFU(IeziG1{ zuN4QGxX=4?9mvf6K9~6$mfu`2BlA7>O*miQZ$0yyAg4TVye$3!MI5B}@1hD8hKBg% zFS$QlhN#9c@xXtRTE;IVDE8iIZGk4GXOI!Yjiu~K?d$>KK=*|AuypR7`-}|s* z|8L&R|G`hiGJgmBEXpho`5Bb?Iq>P}C3}~AQoVHTe#ka&Yx|WsQ_aqWZ1enUtQEcF zUF6i$LGMMUdIvqHDxtkkP5|h-%j}i6HvV5{-t+r?#o@>Q-kC4?>pwMm$y?{Ar7K0dUa9ctGS^&b-tck6*H}hG-ctnwhDieXCLGLJSF*mY`(1-FUVLi0*X(|+c(u2xQ}riq`?qXU?5&@ksjIgXF5Be$YJT8NrBa9k zL$-O`1qY<$RNbKOJ*UK0R=cHkU$I@h$@SGd(v_9{sB-vm-0ZCUi%n#@%^nnZ^H=6f z{&gM{$?00Rm;5uHnj7?g<|(t4|9w(tU#SOqfedA3ouG{QAnuVMklHaFnEn|U-l>A} iUPCn%q$c;<|7TcW6zJBziOn8pG=rzBpUXO@geCw*x=q>u literal 0 HcmV?d00001 diff --git a/res/img/map/laser/laserBendBack.png b/res/img/map/laser/laserBendBack.png new file mode 100644 index 0000000000000000000000000000000000000000..2ffe467127c159a9229cfb86e566942a12730f1c GIT binary patch literal 1204 zcmeAS@N?(olHy`uVBq!ia0y~yU;#226gb#`WY0gYWk8B0-O<;Pfnj4m_n$;oAfL0q zBeIx*f$ty)Gwzs}{T3)FS>hT|5}cn_Ql40p%21G)nOCBhms+A=qGzCIXy%f+{wvTR zLr)jSkczms*B*Mkagbhht3I1#6nH?^>FYj;VlHpJuegEG5`~RQ$PJgnm zwKlcj{-J$FJMt6bCGTwCu%72#^o?+~@49can}3Uan|iR8`}e5_2xav~$(1MG#WSD? z;@A81>i7I49sZkt*%OGe0%F>md&f18eVTAro&nPlxa_n2zzuR8zXEZtJH7fnv%17- z=dyZ6d|pPft?WIsxxuvdH~*M65eg`2iLUl`|KaSfIQ5vK-j~(zcW6t z?|rJcTfPBDJRuy-$k5&cQh5Nc%00jA7#{2gDU4^ptMJV|b_ScR8*Ng)b9dlLJJk#f z^A1GFZTkK6_`Xkypp?Xa6PUVcP5_hAQ|E8MP}z;+it)?#FSR*Z@?Lq$ z@|o}c3qM`CtoOZ!zxmr5*UHY!>_vOdZ23O(Q_$tKpt!tSrm07_?A6FFd#}Iy`}DiN zfyuK}rtaE?YIz2RKR1sb)c<@xXkOZ`|Nc{!&obM$*yiq*&!SINm#yvmWOVsn`YCVU z*`AdkGh$mH+a>kY~e UUM;_C6#z2D)78&qol`;+0JcU<=l}o! literal 0 HcmV?d00001 diff --git a/res/img/map/laser/laserStraight.png b/res/img/map/laser/laserStraight.png new file mode 100644 index 0000000000000000000000000000000000000000..1063ade55acd75face0a535fc3e6f78f72a2379e GIT binary patch literal 1304 zcmeAS@N?(olHy`uVBq!ia0y~yU;#226gb#`WY0gYWk8CtILO_JVcj{Imp~3nx}&cn z1H;CC?mvmFKt5-IM`SSr1K(i~W;~w1A_XWYS>hT|5}cn_Ql40p%21G)nOCBhms+A= zqGzCIXy%f+{wvU+4o?@ykczms*H7o&aS(96`1Ak#c?E)OK0$RW0u@po>hbXwI3J!j zaaa8N@6tds85r(d^|qh%yZe|BnD=K}38VeCE%wtOa>p*Q{>rnQs_^><8-sxfSuC2NK0c7~lA)*vt|<}47I47rSh05%K2 zYIWX%!tKX_OWf!lLl`Bu2^2whu3h5(faZCG8hAX(M^!Omb1yiE!Cp+wtk+>=FyHot z$>JC&1W^qEhYV7DeCS-t{sE`+;QYZDcnDM(1Z1^-#^f&nH+BWoUM2QcC6A`gc zdE#FL(~g}>*>MLU+zqfuJs>`9A2gwYq8Xd92tQ8)CifkCm+BL1J~-Y#EM3a~fN-F| z!xET&JLMBzG8f=VDR7lYsc;i8U;b!YT2HLY;F0#pJ>nO$43;#DHzf3>PUlDdVkII@ j5P=8IO4`5QU)P^HDNcJGYoskOD>8Vx`njxgN@xNA=f!pB literal 0 HcmV?d00001 diff --git a/res/img/map/laser/laserStraightBack.png b/res/img/map/laser/laserStraightBack.png new file mode 100644 index 0000000000000000000000000000000000000000..ca19e15681c30506a11daa20322bf08fc41310ae GIT binary patch literal 1226 zcmeAS@N?(olHy`uVBq!ia0y~yU;#226gb#`WY0gYWk8B0-O<;Pfnj4m_n$;oAfL0q zBeIx*f$tCqGm2_>H2?)AOI#yLg7ec#$`gxH8440J^GfvcQcDy}^bGV2&0I3qe+3%k z?&;zfQW5v|x?!FSBad_7{r{Kvq?vR?T*{cV9{)TV+;Cr@;LY8c{pZi0fB*A>{mH$p zwRa!fJG3vaAU`qQe8=_;>!t5R-?+~EPWR1gw(nx!N}H>>fA2boP*y*~a9zxQ27F4; zP0GLjU-8(EqQCM7h_@zx=e^_hhR3G9j%UDh1G)t;r&@ntO^(_5k2QrTpX68mw)?=} zQ~2~3e*?erw3Eu-lkZMN4{(k!QH-lV$kM?zW2bgEj!X3%a>%KES z(C-Dhn|T5ucUvCKS zWMGiX0)`HM0%qv&F)%dzzaH`p2QZ=4F{GjYq7 zP^@GIO8i(0%sKWAo8dVu|2{iV_(LklD8>Z1QDBEL*qj4e9`69lXXQYZ3_tuqzF`IX z=F_)YkWd9EJMM4T2r?9+ks;m)7$oALum?s25Hy2T>;ncl5H#pZ07ISo1285*fZynt pXxssyhkz#a7&3wAhWSrEKNgQTb$9j4SKBy1BA%{(F6*2UngB+}V}}3$ literal 0 HcmV?d00001 diff --git a/res/img/map/mirror135.png b/res/img/map/mirror135.png new file mode 100644 index 0000000000000000000000000000000000000000..b443f6b04b6f280735b213a2f0f1b676394c8d0a GIT binary patch literal 415 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={WSkfJR9T^xl_H+M9WCij$3p^r= z85sBufiR<}hF1enP_o1|q9iy!t)x7$D3zfgF*C13FE6!3!9>qM&(O>zbNyEa21XfA z7sn6}@2%4extbjWT#K!&yG@!xXKk@;FF(QgHYid(-md0&;oUWlx%iLXd}{0Ku*X(z z{p%OIp35B2Ja4e4HTu-$ZGVL_X6+Vh37vj>X%v%$px%TAsferZgd@HxT2IUpy^tYe zZ*gpc)~y43>Jj~Yg>d&iS$dTUi z@oumpLkzRo&DSgqQU-BC4IaF#J4(zP!~+lKd9X038UA1i+sbw0{4}0#>soJ5{*iOA zqd>89ve0(+h|rh97Mg|CIzdbSGj{_;)DO!%c&T;kANx0kYuuT$uKNK4lfl!~&t;uc GLK6Up$C)kw literal 0 HcmV?d00001 diff --git a/res/img/map/mirror45.png b/res/img/map/mirror45.png new file mode 100644 index 0000000000000000000000000000000000000000..796dfdcaa980e4ecd2e0d401443197b5bbf675e4 GIT binary patch literal 417 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={WSkfJR9T^xl_H+M9WCij$3p^r= z85sBuf-vKbiP>*~f|4b!5hcO-X(i=}MX3x0iJ5sNdU>fO3MP66dWL2$nd`qYFfht_ zx;Tb-cyFC%$kps1;96{D-EGn|)%Vt%!`4q)%2uwKtS^78Fy{E(!dCW2Z$91YQm|`# zSSug<{98FWQhbW?9;ba!ELWdLwtX>N2bZe?^J zG%heMF*ZbLuk`=`0ZBIFTn+T)7=XsBLLbGH)5+7O`X9Js=V z0^D*=bK-85fWKeW!;a_p!{OQS_yS4}6Blgu!}Irm+S{}syw14g{WOQ>0DoKRNu!fe zPsm5j0Wx$^D-D%WkBd=@fTq$=DfO(V1n6{>rt2?RS)@Np@+ryy0000{k%praLUQVc)9pnJ^D-~7eiVbU6Q)(_UjNSEb+;g9Ep105W{P3L5d7kHvpRYT7r~Xa=0NmrW zn?C^ClqE1$g(@S( zo(@g`K!f|kAp^0r2qn0eguXxuh`mBe3X8`9V%W79(y`cqkj|dp-IBl9*OB%BgBG?A?f&Fac?k zDUyoA#cM9<&+Tb!p(1~Y^U7T_IWi^V7DHfwkSF$R%uOvwHD(82ws8w}k#ba5=!al^ z9;{T*-DkqL$t3V#@Ef%y@Tv`jbh#2$G7nQNIT5nl$!1=9-PBjtH3{lA-&6^k<6=Np zzpGVa_%L1M->kxLuyK~z6^uO245-jaz_|K$qWWTO^;;GumE0vc+WCl3Pc(KL*(2LW zqzSDsqfIUxJV}Qp@rXf(zV5zh;(!$~%HeuFI0l@}GhK{gY{}PJi+qj-XCe46wK(F0 zcc!(J5D8{FqbFJN=^(O`wlJ9~oJfoT*4(D`-2=3?2)I^HVH&x`y|+kob>upMErq8| zf(&BgcQ<6KeKAZGI~iycF@mG0m6++$R|+l1BJZaO47~FKhrOVYt)4Oy2kXoQ=HV>w z&S49o_AI9z6F+ycDZcqQze+fi8U-fe0%24V6~{R=m`&)O8E(Hgi@`EoxFsv^L5HYe zmxaV)=vS{|^|90!R6_e;cMmZu=lJJEG#mcRGk|<=4$e3DQ&V6f{h-m25o$#q-=C@orCu-Krfl?M$|1{@Te@$zF4b?99@PIS9mQBj@ICQ#6~_gy zUPWNHcU{?;Oep~q4ML?-mj5%vu0#kYp(06VdX*P0OI(fJO5NJT-jFvGq)dECbvHwWN{^pY{l)R-7}A*zcFoyT(7L$5Tn&LNV54? z#0<_g(D)_G-U*_g6U%*DRgvIi)nb|+vkC`zRik`+c=R7CI^w)LSD7B-9kei68q(mY zByyUF%Min=bTyo9rRusccX`{IEZd-CS`IkaS$RpV6wz3sOD~NlD2ZwVFd}j$Z8?2? zQSDOzP-oUn>^JAvKU^Adb}SzlwZcPlk4qj|f|yNJ&F7S*eezYFlXIt#VYHtvnJ<{E zUaA?1FBx;MQpacI5Z1K^V0RN7#&_%OxOLc!$V2g_rYYi}f;NXmne=dDr;T5b?yIs! zos@!S{S-@gj#Wz+J4rCMk}oi_927bBaOK~yu7|pOlbJb`=4J)t&2pz{9?L%9$6xnA L`?}FjUcU7&DLlR| literal 0 HcmV?d00001 diff --git a/res/img/menu/background.png b/res/img/menu/background.png new file mode 100644 index 0000000000000000000000000000000000000000..5fded32c7813f02c3a2351f8214467d4cb24323c GIT binary patch literal 3887 zcmeHK3s6&68ooC+1ur73W&xoH=+H*1JXV^pD-b~ttwh4Ju&wf1f)PkiXm~6M8FfWb zi11kak%7u_)l)edP zfjlRQ`mU~1=m)mXVN;xEPt35)BT(iQXnUWaT_-CU#(v;$@(`O)NMAH`n(!7wb9k$) z&z~2mu8!2-9`U=*jCyrXFVx&vqHw zD<6AKkyJJ$u3j|dmosp+$(~+y{8GK{o7Q{sL7CV;M7!z0e>#Mk9&arccguU1 z`nJ{T$*-347b%jqHQdXmb*JPZf}xS#Q(;YdwVs@+%Wq_}Xp1wJUvyFSf+asjFPC<8 zu3!UAJ&Rl=`PO7Y9PxLvHo*d-Iw^HInP)ROA8FXaQ${(!1>I(pV8~15&rihiCAaI{ zXoRWs!Rn8-*JSx>m?g^^cB*+D%nQ9#KCHqhKgQdkw594759q}7I2mrzKP2IG>?*T7 zFZ4VQul-bH^gTU+cIO+GSQa4>*0AFh*uOGhua(dx^Os?wzIQ1t9@oTcr1wn3-U?G- z6*z~U3Gp+Xgo+)+HQmWFAV2$Yy@@+&ySY&*j(i7XQn(N^F6ZzXm)$NF%FiP(wM6Zt zR`zVcCQeWKa5Mj@2G!ss8F_eUnL3zDj8vwVXebYJv^$n`CTs;(SBw}O;A~_K%6sW_ z@gcbiqmmaHsM+Dv?XdcmsJ;ca#P%7|w`V*0ldL)YM1JyT`DX1yH?~8(f6jJ+#KGUM z9&obuZ_=lgB+YB7;tbYu0B1mFf|#|Thk&@AJl90ad}_aYv9g2KAT}HMax6LNecJE! z#~Hd)QK15_k)E1=F~Vp&^&Rv__oC-!B=U`QjtXpc1~`NJ@AvXsH~BAV@Iy6>z9KD& zo*FLXYWfh51ZNdu&u|AkBQ5dbwsGPBH zz$hC56x5L}YRUgF!q zQht7JR1wMXAqR z!%GIFbgB9=iy#OQ6;4t`Mt*iEQ90#msy11;JI5Cc#QU=)?7-0k+QHk(EHX}+@l1$* za`Q#!mpkYBJtpBlVA~Nge=1$E3N`&WP0?7|foJ9KA4~P*M$PY>1tB=s?C^oHDq+Xh zGc9EjXRYlbfb{spHZ!UEs3>9V(ZUx(_jn z-zXtT=V_0x;(45}-t?C+yEtUdcmI|@*-)HRQe$S3s*UGwwku~;S8Zl0-N;&2kUZ1# z)|(M_^mx(YP2Vh)SWH`bVExm33S)ZQrIg%D9_6l;(#5JyBUq*^MGRlYwxmVjwVZ=O zWwotPw*lz^Gp9I`A8O3!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK3bUjuq V{*s>2!VR>J!PC{xWt~$(69ByPORWF^ literal 0 HcmV?d00001 diff --git a/res/img/menu/close/pressed.png b/res/img/menu/close/pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..d8fbd3830281955d42da1150ac8e75db4c16fbb9 GIT binary patch literal 228 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`?>!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK3b!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK3b)Db7z#2d@Gxw=wm;KCNn{1`ISV`@ ziy0XB4uLSEsD@VqP*AeOHKHUqKdq!Zu_%?HATcwqL@zJ3M8QPQK+n+3C3F2(1_mY# zPZ!6K4Cl8K9lH)Y@UVRTAOAFUx3;5L;Ph$7Wcv;Irs!SG%RKS=Z{2^JJJbI(|F2u$ z{`=#fgXYgy*B5*Vd(nC7vVUOhm*5-AAAG;B@a@Thf&lr;Hy3{T5*#A`SL9o1Li_9+ z6Pph?)_SD3!<1fcwe?$M&)(YntK%Hs)aCOR{d%!{Lq5~)7?*NBF7~gCb7i&G|Cth7 zd!{zwoa2{gKqLF#zX_;(y8O+Xcz0PPE}J9W-3Ewpaxi?fao7s%ZQI`)+R z&8x@nus@T5suJz{_OaUNto9A_##p^Y)*zQQz{QYUj}U8=%}a6z*`TN`^Y-!glv&{$ z{13k0@A&2<&7fJzF43f9K^T5uHIQSzj=o;$ayy)9=rNm=7_ANhRl`YWo0XD z{!ZEa`$ajDjxWLI-Yve5WXENH%iaBTNUrj?fBMGmmn_H-xpxOYv)`Zd-~PwLb$jaT a7+)>m$enaM%@3Hc7(8A5T-G@yGywpFSxB}3 literal 0 HcmV?d00001 diff --git a/res/img/menu/help/pressed.png b/res/img/menu/help/pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..21da8779bb792241ffcd9e9e5b51be771080c444 GIT binary patch literal 681 zcmeAS@N?(olHy`uVBq!ia0vp^6+o=O!3HF6?%d-6q*&4&eH|GXHuiJ>Nn{1`ISV`@ ziy0XB4uLSEsD@VqP*AeOHKHUqKdq!Zu_%?HATcwqL@zJ3M8QPQK+n+3C3F2(1_mZI zPZ!6K4Cl8K9lH)Y@UVRT@~5U;^@P%^OP6*$HaymNY3VIp-palA-_LLVJ*ob~{r6uB z?C074cvkb3zwaKGZD8du%~kz%T)##B{F6R1Pez3ARkD?J$h^9)SBvK#+Q;{%K-jt{ zz+wge%QpcqrMos9_x$pFK|z50#hVL1eF+Yc|10v%H(`4Bi*t?-7uYS>?EG}<{7tL- zck-XH?6?Oq^5a}tt@VGV%&k4M>W|jN+0BO>ZTCS`?arSSuUJ(KwBdMp*@``%g3G@9 ze%5x$=i_4k`r{nm)bP4f@AhTZwZ1qDG+ORH(7sdtH*X$ab85ZNUP}#`E03+ML*jmF z?*297_klUh5LNpRU9_w7UslQbTW!N>*AlQxH^9XZuHOh3v)XXj_st2A<5OA}*Z!I3 z^O@_L=8wPT9kw7lj+cO4w(j0@g?C^@+rVxw`fA5s=nit|Mu_)Tf4A%a2Uh=EU|>C6 z9<%qn&(`Lox1Z9sfBLGH?@=E(h#{^He_t$6{sa_|x4|CUSv9|ThcbI>^RF8~ zuP>ipR3`ryNyogpQ#WhBSR>i-OEdlZ#rH_AdbxbgUHAPULyjJ{t@!s~_UHfX_IIX! bKd&z|cWLyxHKE^u$%?_#)z4*}Q$iB}A{I`V literal 0 HcmV?d00001 diff --git a/res/img/menu/help/unpressed.png b/res/img/menu/help/unpressed.png new file mode 100644 index 0000000000000000000000000000000000000000..447a6ca6fbfd80da6dbadb7bc7468ef4da126602 GIT binary patch literal 641 zcmeAS@N?(olHy`uVBq!ia0vp^6+o=O!3HF6?%d-6q*&4&eH|GXHuiJ>Nn{1`ISV`@ ziy0XB4uLSEsD@VqP*AeOHKHUqKdq!Zu_%?HATcwqL@zJ3M8QPQK+n+3C3F2(1_s7| zo-U3d8P0DfI(8j);9>dxfBre4B@G%@JaaRTCBy_=a(ue;$NR4Z*>?6n_R7Cc@4eUm zeY4y9EiR_Pn|AG9bMNuptFC(|ZbK3IDt^VfNVb+1`t-cilU66N;iz z_OEe=r1oAGy6V$eU9P&SP-Ru+Z{Dl#q867&Et;iS_X`hR!yy&XiTOk@B-HYwa zlIhG^=3=`0+KaisAkO+y84E&QTOVcw}Q+nn|}BE4_lxk z?l|t9z5!_6?l=36zt;@xeY}3f_8?>he^|fzT>@J5ZqskM{mX$))O*#t#k6X<>Z+MS z-`{zKJoX4FzJ=-(%iP|3nLuYB*}Gfls)f+iIndC8`NuCB)yvD*UbNN)`e{db_#!Fy zMYArWc{URfv@gF`?1u*8$ye`+e%anz4{|*ua^}?af7f0EF>B2`z2)~G|K-a5QnzQm lX_fuG^HTifPrv?R|GmmN&uR9dY+!O>@O1TaS?83{1OQgPHdO!s literal 0 HcmV?d00001 diff --git a/res/img/menu/next/hovered.png b/res/img/menu/next/hovered.png new file mode 100644 index 0000000000000000000000000000000000000000..1248ea022c2ad8488d616fa15967430cf1901360 GIT binary patch literal 232 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucLCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33 zJwr2>%=KS^iXuE+978Pp_x9Z8WH8`h-u*w!FD=_q@ZzoQ1@@kMm>4RY#J6Z$?-5V= zr0IC=v_@0=pCt=zbae0Kc{<#98{fEH_#lVer^#)fHmGbllI)dW?5lNRhx!fn4NS>* WMPh26TRaEa!QkoY=d#Wzp$Py>q)cc4 literal 0 HcmV?d00001 diff --git a/res/img/menu/next/pressed.png b/res/img/menu/next/pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..601fa8efc5fd8e622a1a063aef02f744a2120872 GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucLCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33 zJwr2>%=KS^io!fy978Pp_x40{F(`1bSno35&Lbjt_THgCZ`K7fF!;Y{Dobtu-sEGa zdZJ3w^}~euCR5s%9=)y0$+7#d_J=YhiG$8{mjdLbwM?t<;mld)$twAs0#LT=By}Z;C1rt33 zJwr2>%=KS^ic&pY978Ppm-aAnF(?W!z5hS|o^y!Crl#D4DP^nI%Y9^j(Z^Hj-e$#l z>5kDA&&g#I3J;baYoE4oALq22c^id&o}1WkE-RLL$8!2ix9~n=yDdDWmzKz;teC8D lR@eFN#D(upya@ivd`Bhd#=5Kwpz|0QJYD@<);T3K0RRWDRb2o8 literal 0 HcmV?d00001 diff --git a/res/img/menu/play/hovered.png b/res/img/menu/play/hovered.png new file mode 100644 index 0000000000000000000000000000000000000000..d3e3f20322144f55e37c1f9a574f798012861231 GIT binary patch literal 734 zcmeAS@N?(olHy`uVBq!ia0vp^6+o=O!3HF6?%d-6q*&4&eH|GXHuiJ>Nn{1`ISV`@ ziy0XB4uLSEsD@VqP*AeOHKHUqKdq!Zu_%?HATcwqL@zJ3M8QPQK+n+3C3F2(1_q{R zPZ!6K4Cl8K1GC;Z@URsBkA8b{lE;H~jX7^$R_>MP5%;pc8+_@}`s=U1{w;|A#r*%@ zKDl?tKR4!|bGI+3l>ISpN3HlT&fiZi|CK#;yVw@3bQ?|1=vwW@)-_

k9+Rw3&Cqj)bi@vKYdG>kRbOzkG-0G z)$uz=zpmVOs`K{!g?nrzRAwAsE&FEP*U)#5eucz8J@WQ_z&$x0p)-%e%U0|=6&o}E z)#^EL>;9bLyBfZ;`*m>bRN?LWohz#iJPi8dZ!Y|$RbE)TYJJh8Z{-^K{A`@hK4!7+ zPOqK1dxzcCaLYoNvwoCY->?p_eWth0|7)nqaH-dZn)C{r(sd0YIuYs>o{NQlh8QMmQ|TkT8xj3L2XlXU#5 z-S+h!zt7<@0LAzIZ+6^T{!2?89?0`=IL`0>WT>uPTNbuZ-E}7@jQ%?$vDfOIIzDI3 zx0}HqYA1dA4vda}ok#e7#Z2k9Ed2H6AM5?4D`i2kQLnPG*-rdvvfsb${x|-!Y+cUY p++O{m=E3aG@0soIOuhb{|DYbn_m_`l&jQmTgQu&X%Q~loCIIr}Vlw~$ literal 0 HcmV?d00001 diff --git a/res/img/menu/play/pressed.png b/res/img/menu/play/pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..4558abb9a02925ef8e7152cc9024ef526b34873f GIT binary patch literal 733 zcmeAS@N?(olHy`uVBq!ia0vp^6+o=O!3HF6?%d-6q*&4&eH|GXHuiJ>Nn{1`ISV`@ ziy0XB4uLSEsD@VqP*AeOHKHUqKdq!Zu_%?HATcwqL@zJ3M8QPQK+n+3C3F2(1_q`m zPZ!6K4Cl8KFJ?V);9>dv^7nI3l@E$s>D5*1#G5}lyt)1I{&Yses})yJ$-vn;h;_2+l23H z))$FiU;ax=9qz3EdF<8ftB&6}`gP^LQ=PZ(FWh4*p)%w6?hB!7N@QLXFI;c&daYA< z6Vwk5=MQG(i~fpvVQukxor6DAct4k{@bcYEwR&HC&;8=E5`y^Qhv<)aJ8D^hq7yfp zzkl$X1>y|5jgPbD-wt0=n+{cWq;GX?Ui`#wa|0nEB5b|x+s<0oTjg$$VD^8rNn{1`ISV`@ ziy0XB4uLSEsD@VqP*AeOHKHUqKdq!Zu_%?HATcwqL@zJ3M8QPQK+n+3C3F2(1_mZ= zPZ!6K4Cl8K53YI|z;WQ&|N38iuG~VOGS;s1V!n9P^X`}X?;l^CWB=pE{P)Ig@6Lah zRQ|p#_Kn-r2;+}jzn6KfSt*pg?AVFDZ-w8S)7_KA)Thd$xrT;9hzHxt)%wU+-R7{w20y#;^DIQcMD~&TJqZuq6{dQ6=PcS z9%#X&ch;)ce0nEVM<@ACEkRcnta|tRx+Al+UhWHqSgLw0#Pj=J?TyR4cDDmb^``8-qE0#LT=By}Z;C1rt33 zJwr2>%=KS^ib6eI978Pp_x1*IF(`02@BY8mCUR?n{YITFTyh7Ooli0#LT=By}Z;C1rt33 zJwr2>%=KS^ih?~|978Pp_x4`oVo=~<{$28aPt=C#5n@rS?i*f4o>`M7(e%%&#hz*X zeXa|wtY!m9 RfTlArc)I$ztaD0e0sz_(Ni6^X literal 0 HcmV?d00001 diff --git a/res/img/menu/prev/unpressed.png b/res/img/menu/prev/unpressed.png new file mode 100644 index 0000000000000000000000000000000000000000..d654f98eb130f8964b0bd86d7f6572eb5ae7b7e7 GIT binary patch literal 245 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucLCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33 zJwr2>%=KS^ic&pY978Ppm-aAn9Z=vo^6kIBb)WZ9&(llfR2r?m^_)J+e}zx%ZNfz^ z-QC>XX-77hCtj4*jmrC5;Cee>Qf#h!Rg>%UiSrUJo@f4hc#(C~mOH-^-E^vj#nxWf kw0lDadw1D}U(%0RkLm{9__x;KIM6}{Pgg&ebxsLQ0BR*xa{vGU literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..5e77acb0d05bab1ee8bd73d9450624a1ee046582 GIT binary patch literal 241 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|Ea{HEjtmSN`?>!lvI6;>1s;*b z3=Dh+L6~vJ#O${~LCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33Jwsy?=IAP*qC8I* z#}Etur9FXs2Mjoz%Kv+pYa9#OWETC+Zi>>Epkv%N(~mlsZge>CJ+bWpQ?0_QFFXfX z%M|A-?l@q1O0tQ2&f+;D!nO>hx8^^W{l&Uso1-za-ZbfrPMbxh?Ahgc=((m)49ReQd+}nCv>}i4#ee_(mrq$I*0@9D_)F*ITfd)aX;b{6x&O{}?rSAcSM|Q? zY+M!lDl#c__m#~@R+X(x?+kq#bY5unt)=r+!?t?cdaucvUNtQOqVU=*ztvf5Uv622 z%6NMB&%+0jQUV8pKk)E!JkYFYV`KRsV&lxrRKYcGAtR#=>-h@}4D*`OGaMNF4;q^& zG%P1U`K5#h)w=@v6kh#xvTR!Mo%<=rqJJfvBx3gm$}bqR9!TDD;I(m>BX$pes@(4G?+ec)mw&XIQu#*jUb|8M-J`z) zo^9M+A%DuIO#FSb`77m)?+2y73Lg3Wp!XNYquLK{wJaa)Di+%{RmkrNly}_2e{V&< z!aer%UDmzp?(ljokbfuV0(b&YZ^d{rPGZU>0KVboFyt=akR{0Kg{5uK)l5 literal 0 HcmV?d00001 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; + } +}