├── .gitignore ├── LICENSE ├── NOTICE ├── README.md ├── lib ├── bitmap.jar ├── gson-2.8.5.jar ├── luaj-jse-3.0.1.jar └── vulcdataformat.jar ├── res └── res │ ├── font.fv3 │ ├── icon.png │ ├── icons │ ├── editor │ │ ├── map_editor.png │ │ ├── sprite │ │ │ ├── scope_1.png │ │ │ └── scope_2.png │ │ ├── sprite_editor.png │ │ └── tool │ │ │ ├── bucket.png │ │ │ ├── pencil.png │ │ │ ├── pickup.png │ │ │ └── select.png │ ├── redo.png │ ├── save.png │ ├── selected.png │ ├── shell.png │ └── undo.png │ └── templates │ └── template.zip └── src └── vulc └── luag ├── Console.java ├── LoggerSetup.java ├── editor ├── Editor.java ├── gui │ └── AtlasPreview.java ├── map │ ├── MapCompiler.java │ ├── MapEditor.java │ └── gui │ │ ├── MapAtlasPreview.java │ │ ├── MapPreview.java │ │ ├── MapSizePanel.java │ │ └── MapSizeTextBox.java └── sprite │ ├── SpriteEditor.java │ ├── gui │ ├── SpriteAtlasPreview.java │ ├── SpriteColorbar.java │ ├── SpriteColorbarPalette.java │ ├── SpritePreview.java │ ├── SpriteScopeButton.java │ ├── SpriteScopeSelector.java │ ├── SpriteToolButton.java │ └── SpriteToolbar.java │ ├── history │ └── History.java │ └── tool │ ├── BucketTool.java │ ├── PencilTool.java │ ├── PickupTool.java │ ├── SelectTool.java │ ├── SpriteTool.java │ └── SpriteToolkit.java ├── game ├── Game.java ├── GameSounds.java ├── LuaScriptCore.java ├── cartridge │ └── Cartridge.java ├── interfaces │ ├── Interface001.java │ └── LuaInterface.java ├── map │ └── Map.java └── save │ └── SaveSystem.java ├── gfx ├── Colors.java ├── ConsoleFrame.java ├── Icons.java ├── Screen.java ├── gui │ ├── GUIButton.java │ ├── GUIComponent.java │ ├── GUILabel.java │ ├── GUIMainPanel.java │ ├── GUIPanel.java │ └── GUITextBox.java └── panel │ ├── BootPanel.java │ ├── DeathPanel.java │ ├── EditorPanel.java │ ├── GamePanel.java │ ├── Panel.java │ └── ShellPanel.java ├── input ├── FullScreenListener.java └── InputHandler.java ├── sfx └── Sound.java └── shell ├── Shell.java ├── ShellChar.java ├── ShellRow.java └── command ├── ClsCommand.java ├── EditCommand.java ├── ExitCommand.java ├── FilesCommand.java ├── HelpCommand.java ├── LogCommand.java ├── ModeCommand.java ├── PackCommand.java ├── RunCommand.java ├── SetupCommand.java ├── ShellCommand.java └── VerCommand.java /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /.settings/ 3 | /.project 4 | /.classpath 5 | 6 | /luag*.log* 7 | /*.sav 8 | /console-userdata/ 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | LuaG-Console 2 | Copyright 2019-2022 Vulcalien 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![icon](res/res/icon.png) 2 | # LuaG Console 3 | LuaG is a virtual console that allows you to build a game very quickly using the lua language. 4 | The console offers tools for developers, like the integrated editors for the map and the sprites! 5 | LuaG is always getting updates! 6 | 7 | ## Getting Started 8 | First of all, [download the console](https://github.com/Vulcalien/LuaG-Console/wiki/Download). 9 | **If you are a developer** open the console, type `mode d` and then `setup`. Now you have blank game files, so you can start programming! 10 | Just type `files`, find the `script` folder and there will go all your code! 11 | 12 | **If you want to play** download a game cartridge, put it in the same folder of the *.jar* file, type `run {cartridge-name}` in the console and enjoy! 13 | 14 | ## How to create a Game 15 | Creating a Game in LuaG is very easy! 16 | All Game files are stored inside the folder `console-userdata` (the one you open with the `files` command). Read the [documentation](https://github.com/Vulcalien/LuaG-Console/wiki/Lua-Script) about the game scripting. 17 | You can use useful tools, such as the integrated editors (map and sprites). Just remember to switch to *developer mode* using `mode d`. 18 | In this mode you can **run the game directly** without packing it into a cartridge. You can also exit the game pressing `F8`. 19 | Type `help` to get a list of all the commands you can use. 20 | 21 | ## Game Deployment 22 | To deploy a LuaG game, you have first to create a `cartridge`. You can do this using `pack {cartridge-name}`. 23 | Now, just publish the cartridge! (I suggest you to leave a link to the [download page](https://github.com/Vulcalien/LuaG-Console/wiki/Download) of LuaG console). 24 | 25 | ## License 26 | The Console is released under Apache 2.0 license. See [License](LICENSE). 27 | 28 | ## Built With 29 | - [Bitmap Utility](https://github.com/Vulcalien/Bitmap-Utility) by [Vulcalien](https://github.com/Vulcalien/) 30 | - [Gson](https://github.com/google/gson) by [Google](https://github.com/google) 31 | - [LuaJ](http://www.luaj.org/luaj.html) by [LuaJ](http://www.luaj.org/) 32 | 33 | ## More Details 34 | [Click here](https://github.com/Vulcalien/LuaG-Console/wiki) to see the wiki. 35 | -------------------------------------------------------------------------------- /lib/bitmap.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vulcalien/LuaG-Console-java/b1bdd66a69301d76174f64f66c147b26f5efa5ff/lib/bitmap.jar -------------------------------------------------------------------------------- /lib/gson-2.8.5.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vulcalien/LuaG-Console-java/b1bdd66a69301d76174f64f66c147b26f5efa5ff/lib/gson-2.8.5.jar -------------------------------------------------------------------------------- /lib/luaj-jse-3.0.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vulcalien/LuaG-Console-java/b1bdd66a69301d76174f64f66c147b26f5efa5ff/lib/luaj-jse-3.0.1.jar -------------------------------------------------------------------------------- /lib/vulcdataformat.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vulcalien/LuaG-Console-java/b1bdd66a69301d76174f64f66c147b26f5efa5ff/lib/vulcdataformat.jar -------------------------------------------------------------------------------- /res/res/font.fv3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vulcalien/LuaG-Console-java/b1bdd66a69301d76174f64f66c147b26f5efa5ff/res/res/font.fv3 -------------------------------------------------------------------------------- /res/res/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vulcalien/LuaG-Console-java/b1bdd66a69301d76174f64f66c147b26f5efa5ff/res/res/icon.png -------------------------------------------------------------------------------- /res/res/icons/editor/map_editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vulcalien/LuaG-Console-java/b1bdd66a69301d76174f64f66c147b26f5efa5ff/res/res/icons/editor/map_editor.png -------------------------------------------------------------------------------- /res/res/icons/editor/sprite/scope_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vulcalien/LuaG-Console-java/b1bdd66a69301d76174f64f66c147b26f5efa5ff/res/res/icons/editor/sprite/scope_1.png -------------------------------------------------------------------------------- /res/res/icons/editor/sprite/scope_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vulcalien/LuaG-Console-java/b1bdd66a69301d76174f64f66c147b26f5efa5ff/res/res/icons/editor/sprite/scope_2.png -------------------------------------------------------------------------------- /res/res/icons/editor/sprite_editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vulcalien/LuaG-Console-java/b1bdd66a69301d76174f64f66c147b26f5efa5ff/res/res/icons/editor/sprite_editor.png -------------------------------------------------------------------------------- /res/res/icons/editor/tool/bucket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vulcalien/LuaG-Console-java/b1bdd66a69301d76174f64f66c147b26f5efa5ff/res/res/icons/editor/tool/bucket.png -------------------------------------------------------------------------------- /res/res/icons/editor/tool/pencil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vulcalien/LuaG-Console-java/b1bdd66a69301d76174f64f66c147b26f5efa5ff/res/res/icons/editor/tool/pencil.png -------------------------------------------------------------------------------- /res/res/icons/editor/tool/pickup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vulcalien/LuaG-Console-java/b1bdd66a69301d76174f64f66c147b26f5efa5ff/res/res/icons/editor/tool/pickup.png -------------------------------------------------------------------------------- /res/res/icons/editor/tool/select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vulcalien/LuaG-Console-java/b1bdd66a69301d76174f64f66c147b26f5efa5ff/res/res/icons/editor/tool/select.png -------------------------------------------------------------------------------- /res/res/icons/redo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vulcalien/LuaG-Console-java/b1bdd66a69301d76174f64f66c147b26f5efa5ff/res/res/icons/redo.png -------------------------------------------------------------------------------- /res/res/icons/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vulcalien/LuaG-Console-java/b1bdd66a69301d76174f64f66c147b26f5efa5ff/res/res/icons/save.png -------------------------------------------------------------------------------- /res/res/icons/selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vulcalien/LuaG-Console-java/b1bdd66a69301d76174f64f66c147b26f5efa5ff/res/res/icons/selected.png -------------------------------------------------------------------------------- /res/res/icons/shell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vulcalien/LuaG-Console-java/b1bdd66a69301d76174f64f66c147b26f5efa5ff/res/res/icons/shell.png -------------------------------------------------------------------------------- /res/res/icons/undo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vulcalien/LuaG-Console-java/b1bdd66a69301d76174f64f66c147b26f5efa5ff/res/res/icons/undo.png -------------------------------------------------------------------------------- /res/res/templates/template.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vulcalien/LuaG-Console-java/b1bdd66a69301d76174f64f66c147b26f5efa5ff/res/res/templates/template.zip -------------------------------------------------------------------------------- /src/vulc/luag/Console.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2019-2022 Vulcalien 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy 6 | * of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | ******************************************************************************/ 16 | package vulc.luag; 17 | 18 | import java.awt.Canvas; 19 | import java.awt.Dimension; 20 | import java.awt.Graphics; 21 | import java.awt.Insets; 22 | import java.awt.Toolkit; 23 | import java.awt.image.BufferStrategy; 24 | import java.awt.image.BufferedImage; 25 | import java.awt.image.DataBufferInt; 26 | import java.io.File; 27 | import java.net.URISyntaxException; 28 | import java.util.logging.Level; 29 | import java.util.logging.Logger; 30 | 31 | import javax.swing.JFrame; 32 | 33 | import vulc.luag.gfx.ConsoleFrame; 34 | import vulc.luag.gfx.Screen; 35 | import vulc.luag.gfx.panel.BootPanel; 36 | import vulc.luag.gfx.panel.DeathPanel; 37 | import vulc.luag.gfx.panel.GamePanel; 38 | import vulc.luag.gfx.panel.Panel; 39 | import vulc.luag.gfx.panel.ShellPanel; 40 | import vulc.luag.input.FullScreenListener; 41 | import vulc.luag.shell.Shell; 42 | 43 | /** 44 | * Open Source since: 20.06.2019
45 | * GitHub: https://github.com/Vulcalien/LuaG-Console
46 | * Author: Vulcalien
47 | * 48 | *

Used Libraries

49 | * 55 | */ 56 | public class Console extends Canvas implements Runnable { 57 | 58 | public enum Mode { 59 | USER_GAME, USER_SHELL, DEVELOPER 60 | } 61 | 62 | private static final long serialVersionUID = 1L; 63 | 64 | public static final Object DONT_STOP_LOCK = new Object(); 65 | 66 | public static final String NAME = "LuaG Console"; 67 | public static final String VERSION = "0.7.0-WIP"; 68 | public static final String COPYRIGHT = "Copyright 2022 Vulcalien"; 69 | 70 | public static final int WIDTH = 160, HEIGHT = 160; 71 | public static int scaledWidth, scaledHeight; 72 | public static int xOffset, yOffset; 73 | public static boolean isFullScreen; 74 | 75 | public static final Logger LOGGER = Logger.getLogger(Console.class.getName()); 76 | public static String rootDirectory; 77 | public static String logFile; 78 | 79 | public static final Screen SCREEN = new Screen(WIDTH, HEIGHT); 80 | public static Panel currentPanel; 81 | 82 | public static String cartridge; 83 | public static Mode mode; 84 | 85 | public static Console instance; 86 | public static int ticks = 0; 87 | 88 | private static boolean running = false; 89 | private static Thread thread; 90 | 91 | public static ConsoleFrame frame; 92 | 93 | private final BufferedImage img = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); 94 | private final int[] pixels = ((DataBufferInt) img.getRaster().getDataBuffer()).getData(); 95 | 96 | public void run() { 97 | int ticksPerSecond = 60; 98 | 99 | long nanosPerTick = 1_000_000_000 / ticksPerSecond; 100 | long unprocessedNanos = 0; 101 | long lastTime = System.nanoTime(); 102 | 103 | while(running) { 104 | long now = System.nanoTime(); 105 | long passedTime = now - lastTime; 106 | lastTime = now; 107 | 108 | if(passedTime < 0) passedTime = 0; 109 | if(passedTime > 1_000_000_000) passedTime = 1_000_000_000; 110 | 111 | unprocessedNanos += passedTime; 112 | 113 | while(unprocessedNanos >= nanosPerTick) { 114 | unprocessedNanos -= nanosPerTick; 115 | 116 | try { 117 | tick(); 118 | ticks++; 119 | } catch(Throwable e) { 120 | LOGGER.log(Level.SEVERE, "Console Error", e); 121 | System.exit(1); 122 | } 123 | } 124 | 125 | try { 126 | Thread.sleep(4); 127 | } catch(InterruptedException e) { 128 | e.printStackTrace(); 129 | } 130 | } 131 | } 132 | 133 | public static void start() { 134 | if(running) return; 135 | running = true; 136 | 137 | LOGGER.info("Console: start"); 138 | 139 | thread = new Thread(instance); 140 | thread.start(); 141 | } 142 | 143 | public static void stop() { 144 | if(!running) return; 145 | running = false; 146 | 147 | LOGGER.info("Console: stop"); 148 | 149 | try { 150 | thread.join(); 151 | } catch(InterruptedException e) { 152 | e.printStackTrace(); 153 | } 154 | } 155 | 156 | private void init(String[] args) { 157 | requestFocus(); 158 | 159 | if(args.length > 0) { 160 | if(args[0].equals("-dev")) { 161 | mode = Mode.DEVELOPER; 162 | } else { 163 | // this is not relative to root: it should be an argument passed automatically 164 | cartridge = args[0]; 165 | mode = Mode.USER_GAME; 166 | } 167 | } else { 168 | mode = Mode.USER_SHELL; 169 | } 170 | 171 | LOGGER.info("Startup mode: " + mode); 172 | 173 | Panel nextPanel = null; 174 | 175 | if(mode == Mode.DEVELOPER || mode == Mode.USER_SHELL) { 176 | Shell.init(); 177 | nextPanel = new ShellPanel(); 178 | } else if(mode == Mode.USER_GAME) { 179 | nextPanel = new GamePanel(); 180 | } 181 | 182 | BootPanel bootPanel = new BootPanel(); 183 | bootPanel.nextPanel = nextPanel; 184 | currentPanel = bootPanel; 185 | currentPanel.init(); 186 | } 187 | 188 | private void tick() { 189 | currentPanel.tick(); 190 | 191 | frame.checkSize(); 192 | render(); 193 | } 194 | 195 | private void render() { 196 | BufferStrategy bs = getBufferStrategy(); 197 | if(bs == null) { 198 | createBufferStrategy(3); 199 | return; 200 | } 201 | 202 | for(int i = 0; i < pixels.length; i++) { 203 | pixels[i] = SCREEN.raster.getPixel(i); 204 | } 205 | 206 | Graphics g = bs.getDrawGraphics(); 207 | g.clearRect(0, 0, instance.getWidth(), instance.getHeight()); 208 | g.drawImage(img, xOffset, yOffset, scaledWidth, scaledHeight, null); 209 | g.dispose(); 210 | bs.show(); 211 | } 212 | 213 | public static void switchToPanel(Panel panel) { 214 | LOGGER.info("Switching to panel: " + panel.getClass().getSimpleName()); 215 | 216 | if(currentPanel != null) currentPanel.remove(); 217 | currentPanel = panel; 218 | panel.init(); 219 | panel.onShow(); 220 | } 221 | 222 | public static void die(String text) { 223 | LOGGER.severe("Console die:\n" + text); 224 | 225 | if(mode == Mode.DEVELOPER || mode == Mode.USER_SHELL) { 226 | switchToPanel(new ShellPanel()); 227 | Shell.write(text + "\n\n", Shell.ERROR_FOREGROUND); 228 | } else { 229 | switchToPanel(new DeathPanel(text)); 230 | } 231 | } 232 | 233 | public static void main(String[] args) { 234 | startupOperations(); 235 | LOGGER.info("Starting Console..."); 236 | 237 | Toolkit.getDefaultToolkit().setDynamicLayout(false); 238 | 239 | instance = new Console(); 240 | initFrame(false); 241 | instance.init(args); 242 | 243 | frame.setVisible(true); 244 | start(); 245 | 246 | instance.addKeyListener(new FullScreenListener()); 247 | } 248 | 249 | private static void startupOperations() { 250 | if(Console.class.getResource("Console.class").toString().startsWith("jar")) { 251 | try { 252 | File jarFile = new File(Console.class.getProtectionDomain() 253 | .getCodeSource() 254 | .getLocation() 255 | .toURI()); 256 | rootDirectory = jarFile.getParent() + "/"; 257 | } catch(URISyntaxException e) { 258 | e.printStackTrace(); 259 | } 260 | } else { 261 | rootDirectory = "./"; 262 | } 263 | 264 | LoggerSetup.setup(LOGGER, rootDirectory); 265 | } 266 | 267 | private static void setInitialScale() { 268 | int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height; 269 | 270 | // by default, the console's height is half of the screen 271 | double newScale = (double) screenHeight / HEIGHT / 2; 272 | 273 | updateScaledSize((int) (WIDTH * newScale), (int) (HEIGHT * newScale)); 274 | instance.setSize(scaledWidth, scaledHeight); 275 | frame.pack(); 276 | } 277 | 278 | public static void updateScaledSize(int width, int height) { 279 | // find lowest scale and use it on both sides 280 | int minScaled, minScreen; 281 | if(width * Console.HEIGHT < height * Console.WIDTH) { 282 | minScaled = width; 283 | minScreen = Console.WIDTH; 284 | } else { 285 | minScaled = height; 286 | minScreen = Console.HEIGHT; 287 | } 288 | 289 | scaledWidth = Console.WIDTH * minScaled / minScreen; 290 | scaledHeight = Console.HEIGHT * minScaled / minScreen; 291 | 292 | xOffset = (width - scaledWidth) / 2; 293 | yOffset = (height - scaledHeight) / 2; 294 | } 295 | 296 | private static void initFrame(boolean fullScreen) { 297 | frame = new ConsoleFrame(); 298 | frame.init(); 299 | frame.add(instance); 300 | 301 | isFullScreen = fullScreen; 302 | 303 | if(fullScreen) { 304 | frame.setUndecorated(true); 305 | frame.setExtendedState(JFrame.MAXIMIZED_BOTH); 306 | } else { 307 | // set minimum size (it is console's size not scaled + insets) 308 | frame.pack(); // this sets the insets of the frame 309 | 310 | Insets insets = frame.getInsets(); 311 | frame.setMinimumSize(new Dimension(WIDTH + insets.left + insets.right, 312 | HEIGHT + insets.top + insets.bottom)); 313 | 314 | setInitialScale(); 315 | frame.setLocationRelativeTo(null); 316 | } 317 | } 318 | 319 | public static void switchFullScreen() { 320 | stop(); 321 | 322 | frame.setVisible(false); 323 | 324 | // this changes the value of isFullScreen 325 | initFrame(!isFullScreen); 326 | 327 | frame.setVisible(true); 328 | instance.requestFocus(); 329 | 330 | start(); 331 | } 332 | 333 | } 334 | -------------------------------------------------------------------------------- /src/vulc/luag/LoggerSetup.java: -------------------------------------------------------------------------------- 1 | package vulc.luag; 2 | 3 | import java.io.File; 4 | import java.util.Locale; 5 | import java.util.logging.FileHandler; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | import java.util.logging.SimpleFormatter; 9 | 10 | public abstract class LoggerSetup { 11 | 12 | public static final void setup(Logger logger, String folder) { 13 | logger.setLevel(Level.ALL); 14 | Locale.setDefault(Locale.ENGLISH); 15 | try { 16 | // fallback value, in case a good filename is not found 17 | String fileName = folder + "luag.log"; 18 | 19 | for(int i = 0; i < 100; i++) { 20 | // first, try with "luag.log" then try adding numbers 21 | fileName = folder + "luag" + (i == 0 ? "" : "-" + i) + ".log"; 22 | File file = new File(fileName); 23 | 24 | // if does not exist or is a file and is not locked 25 | if(!file.exists() || (file.isFile() && file.delete())) { 26 | break; 27 | } 28 | } 29 | 30 | Console.logFile = fileName; 31 | FileHandler fh = new FileHandler(fileName); 32 | fh.setFormatter(new SimpleFormatter()); 33 | logger.addHandler(fh); 34 | } catch(Exception e) { 35 | e.printStackTrace(); 36 | } 37 | 38 | Runtime.getRuntime().addShutdownHook(new Thread() { 39 | public void run() { 40 | logger.info("Shutting down Console"); 41 | } 42 | }); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/Editor.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor; 2 | 3 | import vulc.luag.gfx.gui.GUIPanel; 4 | import vulc.luag.gfx.panel.EditorPanel; 5 | import vulc.luag.input.InputHandler; 6 | 7 | public abstract class Editor { 8 | 9 | public final EditorPanel editorPanel; 10 | 11 | protected final GUIPanel guiPanel; 12 | protected final InputHandler input; 13 | 14 | public Editor(EditorPanel editorPanel, int x, int y, int w, int h) { 15 | this.editorPanel = editorPanel; 16 | this.guiPanel = new GUIPanel(x, y, w, h); 17 | this.input = editorPanel.mainPanel.input; 18 | } 19 | 20 | public void onShow() { 21 | editorPanel.mainPanel.add(this.guiPanel); 22 | } 23 | 24 | public void tick() { 25 | } 26 | 27 | public abstract String getTitle(); 28 | 29 | public void remove() { 30 | editorPanel.mainPanel.remove(this.guiPanel); 31 | } 32 | 33 | public boolean shouldSave() { 34 | return false; 35 | } 36 | 37 | public void onSave() { 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/gui/AtlasPreview.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.gui; 2 | 3 | import vulc.luag.game.Game; 4 | import vulc.luag.gfx.Icons; 5 | import vulc.luag.gfx.gui.GUIPanel; 6 | 7 | public class AtlasPreview extends GUIPanel { 8 | 9 | private final Game game; 10 | 11 | private int animationTicks = 0; 12 | protected int atlasOffset = 0; 13 | protected int selected = 0; 14 | public int scope = 1; 15 | 16 | public AtlasPreview(int x, int y, int w, int h, Game game) { 17 | super(x, y, w, h); 18 | this.game = game; 19 | } 20 | 21 | public void tick() { 22 | animationTicks++; 23 | } 24 | 25 | public void drawComponents() { 26 | screen.draw(game.atlas.getSubimage(0, atlasOffset * Game.SPR_SIZE, w, h), 0, 0); 27 | 28 | int xSpr = selected % 16; 29 | int ySpr = ((selected / 16) - atlasOffset); 30 | 31 | int transparency = animationTicks / 50 % 2 == 0 ? 0xaa : 0xdd; 32 | 33 | screen.drawBool(Icons.SELECTED.getScaled(scope), 0xffffff, transparency, 34 | xSpr * Game.SPR_SIZE, 35 | ySpr * Game.SPR_SIZE); 36 | } 37 | 38 | public void onMouseDown(int xMouse, int yMouse) { 39 | int xs = xMouse / Game.SPR_SIZE; 40 | int ys = yMouse / Game.SPR_SIZE + atlasOffset; 41 | 42 | setSelected(xs, ys); 43 | } 44 | 45 | public void onMouseScroll(int xMouse, int yMouse, int count) { 46 | int newOffset = atlasOffset + count; 47 | if(newOffset >= 0 && newOffset + h / Game.SPR_SIZE <= 16) { 48 | atlasOffset = newOffset; 49 | } 50 | } 51 | 52 | public void setSelected(int xs, int ys) { 53 | if(xs + scope > 16) xs = 16 - scope; 54 | if(ys + scope > 16) ys = 16 - scope; 55 | 56 | selected = xs + ys * 16; // 16 = atlas.width (in sprites) 57 | } 58 | 59 | public void setScope(int scope) { 60 | this.scope = scope; 61 | 62 | // check if the selected area is out of bounds 63 | int xs = selected % 16; 64 | int ys = selected / 16; 65 | 66 | setSelected(xs, ys); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/map/MapCompiler.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.map; 2 | 3 | import java.io.BufferedOutputStream; 4 | import java.io.DataOutputStream; 5 | import java.io.File; 6 | import java.io.FileOutputStream; 7 | import java.io.IOException; 8 | 9 | import vulc.luag.Console; 10 | import vulc.luag.game.Game; 11 | import vulc.luag.game.map.Map; 12 | 13 | public abstract class MapCompiler { 14 | 15 | public static void compile(Map map) { 16 | synchronized(Console.DONT_STOP_LOCK) { 17 | try { 18 | File file = new File(Game.MAP_FILE); 19 | file.createNewFile(); 20 | 21 | DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file))); 22 | 23 | int w = map.width; 24 | int h = map.height; 25 | 26 | out.writeInt(w); 27 | out.writeInt(h); 28 | 29 | for(int i = 0; i < map.tiles.length; i++) { 30 | out.writeByte(map.tiles[i] + 128); 31 | } 32 | out.close(); 33 | } catch(IOException e) { 34 | e.printStackTrace(); 35 | } 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/map/MapEditor.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.map; 2 | 3 | import java.awt.event.KeyEvent; 4 | 5 | import vulc.luag.editor.Editor; 6 | import vulc.luag.editor.map.gui.MapAtlasPreview; 7 | import vulc.luag.editor.map.gui.MapPreview; 8 | import vulc.luag.editor.map.gui.MapSizePanel; 9 | import vulc.luag.editor.map.gui.MapSizeTextBox; 10 | import vulc.luag.game.Game; 11 | import vulc.luag.game.map.Map; 12 | import vulc.luag.gfx.gui.GUIPanel; 13 | import vulc.luag.gfx.panel.EditorPanel; 14 | import vulc.luag.input.InputHandler.Key; 15 | import vulc.luag.input.InputHandler.KeyType; 16 | 17 | public class MapEditor extends Editor { 18 | 19 | private final Key moveUp; 20 | private final Key moveLeft; 21 | private final Key moveDown; 22 | private final Key moveRight; 23 | 24 | public int xOffset = 0, yOffset = 0; 25 | public int selectedTile = 0; 26 | 27 | public boolean shouldSaveContent = false; 28 | 29 | public MapEditor(EditorPanel editorPanel, int x, int y, int w, int h) { 30 | super(editorPanel, x, y, w, h); 31 | 32 | moveUp = input.new Key(KeyType.KEYBOARD, KeyEvent.VK_W); 33 | moveLeft = input.new Key(KeyType.KEYBOARD, KeyEvent.VK_A); 34 | moveDown = input.new Key(KeyType.KEYBOARD, KeyEvent.VK_S); 35 | moveRight = input.new Key(KeyType.KEYBOARD, KeyEvent.VK_D); 36 | 37 | // INTERFACE 38 | guiPanel.background = 0x000000; 39 | 40 | GUIPanel previewPanel = new MapPreview(5, 5, guiPanel.w - 10, 10 * Game.SPR_SIZE, 41 | this); 42 | guiPanel.add(previewPanel); 43 | 44 | int wAtlas = editorPanel.game.atlas.width; 45 | int hAtlas = 4 * Game.SPR_SIZE; 46 | MapAtlasPreview atlasPreview = new MapAtlasPreview((guiPanel.w - wAtlas) / 2, guiPanel.h - hAtlas - 5, 47 | wAtlas, hAtlas, 48 | this); 49 | guiPanel.add(atlasPreview); 50 | 51 | GUIPanel sizePanel = new MapSizePanel(atlasPreview.x, previewPanel.y + previewPanel.h + 3, 52 | atlasPreview.w, 12, 53 | this); 54 | guiPanel.add(sizePanel); 55 | } 56 | 57 | public void tick() { 58 | int moveSpeed = 2; 59 | if(moveUp.isKeyDown()) yOffset -= moveSpeed; 60 | if(moveLeft.isKeyDown()) xOffset -= moveSpeed; 61 | if(moveDown.isKeyDown()) yOffset += moveSpeed; 62 | if(moveRight.isKeyDown()) xOffset += moveSpeed; 63 | } 64 | 65 | public void resizeMap(int newSide, boolean sideFlag) { 66 | Map oldMap = editorPanel.game.map; 67 | 68 | int w = (sideFlag == MapSizeTextBox.WIDTH ? newSide : oldMap.width); 69 | int h = (sideFlag == MapSizeTextBox.HEIGHT ? newSide : oldMap.height); 70 | Map newMap = new Map(w, h); 71 | 72 | x_loop: 73 | for(int x = 0; x < oldMap.width; x++) { 74 | if(x >= w) break x_loop; 75 | 76 | y_loop: 77 | for(int y = 0; y < oldMap.height; y++) { 78 | if(y >= h) break y_loop; 79 | 80 | newMap.setTile(x, y, oldMap.getTile(x, y)); 81 | } 82 | } 83 | 84 | editorPanel.game.map = newMap; 85 | shouldSaveContent = true; 86 | } 87 | 88 | public boolean shouldSave() { 89 | return shouldSaveContent; 90 | } 91 | 92 | public void onSave() { 93 | MapCompiler.compile(editorPanel.game.map); 94 | shouldSaveContent = false; 95 | } 96 | 97 | public String getTitle() { 98 | return "Map Editor"; 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/map/gui/MapAtlasPreview.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.map.gui; 2 | 3 | import vulc.luag.editor.gui.AtlasPreview; 4 | import vulc.luag.editor.map.MapEditor; 5 | 6 | public class MapAtlasPreview extends AtlasPreview { 7 | 8 | private final MapEditor editor; 9 | 10 | public MapAtlasPreview(int x, int y, int w, int h, MapEditor editor) { 11 | super(x, y, w, h, editor.editorPanel.game); 12 | this.editor = editor; 13 | } 14 | 15 | public void onMouseDown(int xMouse, int yMouse) { 16 | super.onMouseDown(xMouse, yMouse); 17 | editor.selectedTile = selected; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/map/gui/MapPreview.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.map.gui; 2 | 3 | import vulc.luag.editor.map.MapEditor; 4 | import vulc.luag.game.Game; 5 | import vulc.luag.gfx.Colors; 6 | import vulc.luag.gfx.Screen; 7 | import vulc.luag.gfx.gui.GUIPanel; 8 | 9 | public class MapPreview extends GUIPanel { 10 | 11 | private final MapEditor editor; 12 | private final Game game; 13 | 14 | private int xPointed, yPointed; 15 | 16 | public MapPreview(int x, int y, int w, int h, MapEditor editor) { 17 | super(x, y, w, h); 18 | this.editor = editor; 19 | this.game = editor.editorPanel.game; 20 | 21 | background = Colors.BACKGROUND_1; 22 | } 23 | 24 | protected void drawComponents() { 25 | super.drawComponents(); 26 | game.map.render(this.screen, game, editor.xOffset, editor.yOffset, 1); 27 | 28 | String xText = "x: " + xPointed; 29 | String yText = "y: " + yPointed; 30 | 31 | int wPanel = Math.max(Screen.FONT.widthOf(xText), Screen.FONT.widthOf(yText)); 32 | int hPanel = Screen.FONT.getHeight() * 2 + 1; 33 | 34 | screen.fill(w - wPanel, h - hPanel, w, h, Colors.BACKGROUND_1); 35 | 36 | screen.write(xText, Colors.FOREGROUND_1, 37 | w - wPanel, h - hPanel); 38 | screen.write(yText, Colors.FOREGROUND_1, 39 | w - wPanel, h - Screen.FONT.getHeight()); 40 | } 41 | 42 | public void onMouseDown(int xMouse, int yMouse) { 43 | super.onMouseDown(xMouse, yMouse); 44 | 45 | int xt = Math.floorDiv(xMouse + editor.xOffset, Game.SPR_SIZE); 46 | int yt = Math.floorDiv(yMouse + editor.yOffset, Game.SPR_SIZE); 47 | 48 | if(xt < 0 || yt < 0 || xt >= game.map.width || yt >= game.map.height) return; 49 | 50 | if(game.map.getTile(xt, yt) != editor.selectedTile) { 51 | game.map.setTile(xt, yt, editor.selectedTile); 52 | editor.shouldSaveContent = true; 53 | } 54 | } 55 | 56 | public void onMouseInside(int xMouse, int yMouse) { 57 | this.xPointed = Math.floorDiv(xMouse + editor.xOffset, Game.SPR_SIZE); 58 | this.yPointed = Math.floorDiv(yMouse + editor.yOffset, Game.SPR_SIZE); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/map/gui/MapSizePanel.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.map.gui; 2 | 3 | import vulc.luag.editor.map.MapEditor; 4 | import vulc.luag.game.map.Map; 5 | import vulc.luag.gfx.Colors; 6 | import vulc.luag.gfx.Screen; 7 | import vulc.luag.gfx.gui.GUILabel; 8 | import vulc.luag.gfx.gui.GUIPanel; 9 | 10 | public class MapSizePanel extends GUIPanel { 11 | 12 | public MapSizePanel(int x, int y, int w, int h, MapEditor editor) { 13 | super(x, y, w, h); 14 | 15 | Map map = editor.editorPanel.game.map; 16 | 17 | int textBoxWidth = (Screen.FONT.widthOf(' ') + Screen.FONT.getLetterSpacing()) * MapSizeTextBox.N_CHARS + 1; 18 | 19 | GUIPanel wPanel = new GUIPanel(0, 0, w / 2, h); 20 | { 21 | wPanel.background = Colors.BACKGROUND_0; 22 | 23 | GUILabel label = new GUILabel(0, 0, Screen.FONT.widthOf("width"), h); 24 | label.textColor = Colors.FOREGROUND_0; 25 | label.text = "width"; 26 | wPanel.add(label); 27 | 28 | MapSizeTextBox textBox = new MapSizeTextBox(label.w + 3, 1, 29 | textBoxWidth, h - 2, 30 | editor, MapSizeTextBox.WIDTH); 31 | textBox.text = map.width + ""; 32 | wPanel.add(textBox); 33 | } 34 | this.add(wPanel); 35 | 36 | GUIPanel hPanel = new GUIPanel(w / 2, 0, w / 2, h); 37 | { 38 | hPanel.background = Colors.BACKGROUND_0; 39 | 40 | GUILabel label = new GUILabel(0, 0, Screen.FONT.widthOf("height"), h); 41 | label.textColor = Colors.FOREGROUND_0; 42 | label.text = "height"; 43 | hPanel.add(label); 44 | 45 | MapSizeTextBox textBox = new MapSizeTextBox(label.w + 3, 1, 46 | textBoxWidth, h - 2, 47 | editor, MapSizeTextBox.HEIGHT); 48 | textBox.text = map.height + ""; 49 | hPanel.add(textBox); 50 | } 51 | this.add(hPanel); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/map/gui/MapSizeTextBox.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.map.gui; 2 | 3 | import vulc.luag.editor.map.MapEditor; 4 | import vulc.luag.gfx.gui.GUITextBox; 5 | 6 | public class MapSizeTextBox extends GUITextBox { 7 | 8 | public static final boolean WIDTH = false, HEIGHT = true; 9 | public static final int N_CHARS = 4; 10 | 11 | private final MapEditor editor; 12 | private final boolean side; 13 | 14 | public MapSizeTextBox(int x, int y, int w, int h, MapEditor editor, boolean side) { 15 | super(x, y, w, h); 16 | this.editor = editor; 17 | this.side = side; 18 | 19 | acceptedText = GUITextBox.DEC_ONLY; 20 | nChars = N_CHARS; 21 | opaque = true; 22 | background = 0xffffff; 23 | textColor = 0x000000; 24 | } 25 | 26 | public void onEnterPress() { 27 | super.onEnterPress(); 28 | 29 | int value; 30 | if(text.equals("")) { 31 | value = 0; 32 | } else { 33 | value = Integer.parseInt(text); 34 | if(value > 256) { 35 | value = 256; 36 | } 37 | } 38 | text = "" + value; 39 | 40 | editor.resizeMap(value, side); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/sprite/SpriteEditor.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.sprite; 2 | 3 | import java.awt.event.KeyEvent; 4 | import java.awt.image.BufferedImage; 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import javax.imageio.ImageIO; 11 | 12 | import vulc.bitmap.Bitmap; 13 | import vulc.luag.editor.Editor; 14 | import vulc.luag.editor.sprite.gui.SpriteAtlasPreview; 15 | import vulc.luag.editor.sprite.gui.SpriteColorbar; 16 | import vulc.luag.editor.sprite.gui.SpritePreview; 17 | import vulc.luag.editor.sprite.gui.SpriteScopeSelector; 18 | import vulc.luag.editor.sprite.gui.SpriteToolbar; 19 | import vulc.luag.editor.sprite.history.History; 20 | import vulc.luag.editor.sprite.tool.SpriteToolkit; 21 | import vulc.luag.game.Game; 22 | import vulc.luag.gfx.gui.GUIComponent; 23 | import vulc.luag.gfx.gui.GUIPanel; 24 | import vulc.luag.gfx.gui.GUITextBox; 25 | import vulc.luag.gfx.panel.EditorPanel; 26 | import vulc.luag.input.InputHandler.Key; 27 | import vulc.luag.input.InputHandler.KeyType; 28 | 29 | public class SpriteEditor extends Editor { 30 | 31 | public static final int PALETTE_SIZE = 8; 32 | 33 | public static final int DEFAULT_SCALE = 6; 34 | 35 | // preview and atlas 36 | public final SpriteAtlasPreview atlasPreview; 37 | public final Bitmap atlas; 38 | public Bitmap preview; 39 | public int spriteID = 0; 40 | public int scope = 1; 41 | 42 | public final SpriteToolkit toolkit = new SpriteToolkit(); 43 | private final Key ctrl, p, f, k, s, 44 | z, y, c, v, 45 | up, left, down, right; 46 | 47 | // select color and last colors 48 | public int selectedColor; 49 | public final List lastColors = new ArrayList(); 50 | 51 | // editing history 52 | public final History history = new History(this, 100); 53 | public boolean isEditing = false, wasEditing = false; 54 | public boolean shouldSaveContent = false; 55 | 56 | // selection and copy/paste 57 | public int selx0, sely0, selx1, sely1; 58 | public boolean pasteMode = false; 59 | public Bitmap copied; 60 | public int xPasted, yPasted; 61 | 62 | public GUITextBox selectColorTxt; 63 | 64 | public SpriteEditor(EditorPanel panel, int x, int y, int w, int h) { 65 | super(panel, x, y, w, h); 66 | this.atlas = panel.game.atlas; 67 | 68 | // keys 69 | this.ctrl = input.new Key(KeyType.KEYBOARD, KeyEvent.VK_CONTROL); 70 | this.p = input.new Key(KeyType.KEYBOARD, KeyEvent.VK_P); 71 | this.f = input.new Key(KeyType.KEYBOARD, KeyEvent.VK_F); 72 | this.k = input.new Key(KeyType.KEYBOARD, KeyEvent.VK_K); 73 | this.s = input.new Key(KeyType.KEYBOARD, KeyEvent.VK_S); 74 | 75 | this.z = input.new Key(KeyType.KEYBOARD, KeyEvent.VK_Z); 76 | this.y = input.new Key(KeyType.KEYBOARD, KeyEvent.VK_Y); 77 | this.c = input.new Key(KeyType.KEYBOARD, KeyEvent.VK_C); 78 | this.v = input.new Key(KeyType.KEYBOARD, KeyEvent.VK_V); 79 | 80 | this.up = input.new Key(KeyType.KEYBOARD, KeyEvent.VK_UP); 81 | this.left = input.new Key(KeyType.KEYBOARD, KeyEvent.VK_LEFT); 82 | this.down = input.new Key(KeyType.KEYBOARD, KeyEvent.VK_DOWN); 83 | this.right = input.new Key(KeyType.KEYBOARD, KeyEvent.VK_RIGHT); 84 | 85 | preview = panel.game.getSprite(spriteID, scope, scope); 86 | 87 | // default palette 88 | lastColors.add(0x00_00_00); 89 | lastColors.add(0xff_ff_ff); 90 | lastColors.add(0xff_00_00); 91 | lastColors.add(0x00_ff_00); 92 | lastColors.add(0x00_00_ff); 93 | lastColors.add(0xff_ff_00); 94 | lastColors.add(0xff_00_ff); 95 | lastColors.add(0x00_ff_ff); 96 | 97 | history.save(); 98 | 99 | // INTERFACE 100 | guiPanel.background = 0x000000; 101 | 102 | int previewSize = Game.SPR_SIZE * DEFAULT_SCALE; 103 | GUIComponent sprPreview = new SpritePreview((guiPanel.w - previewSize) / 2 - SpritePreview.BORDER, 5, 104 | previewSize + SpritePreview.BORDER * 2, 105 | previewSize + SpritePreview.BORDER * 2, 106 | this); 107 | guiPanel.add(sprPreview); 108 | 109 | int scopes[] = {1, 2}; 110 | SpriteScopeSelector scopeSelector = new SpriteScopeSelector(sprPreview.x - 9 - 5, 111 | (sprPreview.y + sprPreview.h) / 2 - 5, 112 | 10, 1 + scopes.length * 9, 113 | this, scopes); 114 | guiPanel.add(scopeSelector); 115 | 116 | int hAtlas = 8 * Game.SPR_SIZE; 117 | atlasPreview = new SpriteAtlasPreview((guiPanel.w - atlas.width) / 2, guiPanel.h - hAtlas - 5, 118 | atlas.width, hAtlas, 119 | this); 120 | guiPanel.add(atlasPreview); 121 | 122 | int hToolbar = 9 * 5 + 1; 123 | GUIPanel toolbar = new SpriteToolbar(atlasPreview.x, sprPreview.y + (sprPreview.h - hToolbar) / 2, 124 | 19, hToolbar, 125 | this); 126 | guiPanel.add(toolbar); 127 | 128 | int xColorbar = sprPreview.x + sprPreview.w + 5; 129 | GUIPanel colorbar = new SpriteColorbar(xColorbar, 5, 130 | guiPanel.w - xColorbar - 5, sprPreview.h, 131 | this); 132 | guiPanel.add(colorbar); 133 | 134 | toolkit.setTool(toolkit.pencil); 135 | selectColor(0xffffff); 136 | } 137 | 138 | public void tick() { 139 | if(ctrl.isKeyDown()) { 140 | if(z.isPressed()) undo(); 141 | if(y.isPressed()) redo(); 142 | 143 | if(c.isPressed() && toolkit.currentTool == toolkit.select) copy(); 144 | if(v.isPressed()) paste(); 145 | } else { 146 | if(p.isPressed()) toolkit.setTool(toolkit.pencil); 147 | if(f.isPressed()) toolkit.setTool(toolkit.bucket); 148 | if(k.isPressed()) toolkit.setTool(toolkit.pickup); 149 | if(s.isPressed()) toolkit.setTool(toolkit.select); 150 | 151 | if(up.isPressed()) moveSelected(0, -1); 152 | if(left.isPressed()) moveSelected(-1, 0); 153 | if(down.isPressed()) moveSelected(0, +1); 154 | if(right.isPressed()) moveSelected(+1, 0); 155 | } 156 | 157 | if(pasteMode && toolkit.currentTool != toolkit.select) { 158 | endPaste(); 159 | } 160 | 161 | boolean shouldSave = wasEditing && !isEditing; 162 | wasEditing = isEditing; 163 | isEditing = false; 164 | 165 | if(shouldSave) { 166 | saveHistory(); 167 | } 168 | } 169 | 170 | public void updatePreview() { 171 | preview = atlasPreview.getPreview(); 172 | } 173 | 174 | private void saveHistory() { 175 | // update atlas and then history.save will record it 176 | atlas.draw(preview, (spriteID % 16) * Game.SPR_SIZE, (spriteID / 16) * Game.SPR_SIZE); 177 | history.save(); 178 | } 179 | 180 | public String getTitle() { 181 | return "Sprite Editor"; 182 | } 183 | 184 | public void selectColor(int color) { 185 | if(!lastColors.contains(selectedColor)) { 186 | lastColors.add(0, selectedColor); 187 | lastColors.remove(PALETTE_SIZE); 188 | } 189 | this.selectedColor = color; 190 | 191 | String colorString = Integer.toString(color, 16); 192 | while(colorString.length() < 6) { 193 | colorString = "0" + colorString; 194 | } 195 | selectColorTxt.text = colorString; 196 | } 197 | 198 | public boolean shouldSave() { 199 | return shouldSaveContent; 200 | } 201 | 202 | public void onSave() { 203 | try { 204 | int[] pixels = new int[atlas.width * atlas.height]; 205 | for(int i = 0; i < atlas.size(); i++) { 206 | pixels[i] = atlas.raster.getPixel(i); 207 | } 208 | 209 | BufferedImage img = new BufferedImage(atlas.width, atlas.height, BufferedImage.TYPE_INT_RGB); 210 | img.setRGB(0, 0, atlas.width, atlas.height, pixels, 0, atlas.width); 211 | 212 | ImageIO.write(img, "png", new File(Game.ATLAS_FILE)); 213 | shouldSaveContent = false; 214 | } catch(IOException e) { 215 | e.printStackTrace(); 216 | } 217 | } 218 | 219 | public void undo() { 220 | if(history.undo()) { 221 | updatePreview(); 222 | shouldSaveContent = true; 223 | } 224 | } 225 | 226 | public void redo() { 227 | if(history.redo()) { 228 | updatePreview(); 229 | shouldSaveContent = true; 230 | } 231 | } 232 | 233 | public void copy() { 234 | fixCopySelection(); 235 | copied = preview.getSubimage(selx0, sely0, selx1 - selx0 + 1, sely1 - sely0 + 1); 236 | } 237 | 238 | public void paste() { 239 | if(copied == null) return; 240 | if(pasteMode) endPaste(); 241 | 242 | pasteMode = true; 243 | toolkit.setTool(toolkit.select); 244 | xPasted = 0; 245 | yPasted = 0; 246 | 247 | selx0 = xPasted; 248 | sely0 = yPasted; 249 | selx1 = xPasted + copied.width - 1; 250 | sely1 = yPasted + copied.height - 1; 251 | } 252 | 253 | public void endPaste() { 254 | if(!pasteMode) return; 255 | 256 | pasteMode = false; 257 | preview.draw(copied, xPasted, yPasted); 258 | 259 | saveHistory(); 260 | shouldSaveContent = true; 261 | } 262 | 263 | public void moveSelected(int x, int y) { 264 | if(pasteMode) { 265 | xPasted += x; 266 | yPasted += y; 267 | 268 | selx0 += x; 269 | sely0 += y; 270 | selx1 += x; 271 | sely1 += y; 272 | } else if(toolkit.currentTool == toolkit.select) { 273 | copy(); 274 | 275 | pasteMode = true; 276 | xPasted = selx0; 277 | yPasted = sely0; 278 | 279 | // at this point, selection was already fixed, in copy() 280 | preview.fill(selx0, sely0, selx1, sely1, 0xff00ff); 281 | 282 | // after setting pasteMode it's necessary to move the pasted bitmap 283 | moveSelected(x, y); 284 | } 285 | } 286 | 287 | private void fixCopySelection() { 288 | if(selx0 < 0) selx0 = 0; 289 | if(sely0 < 0) sely0 = 0; 290 | if(selx1 < 0) selx1 = 0; 291 | if(sely1 < 0) sely1 = 0; 292 | 293 | if(selx0 >= preview.width) selx0 = preview.width - 1; 294 | if(sely0 >= preview.height) sely0 = preview.height - 1; 295 | if(selx1 >= preview.width) selx1 = preview.width - 1; 296 | if(sely1 >= preview.height) sely1 = preview.height - 1; 297 | 298 | int x0 = Math.min(selx0, selx1); 299 | int y0 = Math.min(sely0, sely1); 300 | int x1 = Math.max(selx0, selx1); 301 | int y1 = Math.max(sely0, sely1); 302 | 303 | selx0 = x0; 304 | sely0 = y0; 305 | selx1 = x1; 306 | sely1 = y1; 307 | } 308 | 309 | public void setScope(int scope) { 310 | this.scope = scope; 311 | 312 | atlasPreview.setScope(scope); 313 | 314 | updatePreview(); 315 | } 316 | 317 | } 318 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/sprite/gui/SpriteAtlasPreview.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.sprite.gui; 2 | 3 | import vulc.bitmap.Bitmap; 4 | import vulc.luag.editor.gui.AtlasPreview; 5 | import vulc.luag.editor.sprite.SpriteEditor; 6 | import vulc.luag.game.Game; 7 | 8 | public class SpriteAtlasPreview extends AtlasPreview { 9 | 10 | private final SpriteEditor editor; 11 | 12 | public SpriteAtlasPreview(int x, int y, int w, int h, SpriteEditor editor) { 13 | super(x, y, w, h, editor.editorPanel.game); 14 | this.editor = editor; 15 | } 16 | 17 | public Bitmap getPreview() { 18 | Game game = editor.editorPanel.game; 19 | return game.getSprite(selected, editor.scope, editor.scope); 20 | } 21 | 22 | public void setSelected(int xs, int ys) { 23 | super.setSelected(xs, ys); 24 | 25 | editor.endPaste(); 26 | 27 | editor.spriteID = selected; 28 | editor.updatePreview(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/sprite/gui/SpriteColorbar.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.sprite.gui; 2 | 3 | import vulc.luag.editor.sprite.SpriteEditor; 4 | import vulc.luag.gfx.Colors; 5 | import vulc.luag.gfx.Screen; 6 | import vulc.luag.gfx.gui.GUIComponent; 7 | import vulc.luag.gfx.gui.GUIPanel; 8 | import vulc.luag.gfx.gui.GUITextBox; 9 | 10 | public class SpriteColorbar extends GUIPanel { 11 | 12 | public SpriteColorbar(int x, int y, int w, int h, SpriteEditor editor) { 13 | super(x, y, w, h); 14 | 15 | this.background = Colors.BACKGROUND_0; 16 | 17 | GUIComponent colorPreview = new GUIComponent((w - 16) / 2, 1, 16, 16) { 18 | public void render(Screen screen) { 19 | screen.fill(x, y, x + w - 1, y + h - 1, editor.selectedColor); 20 | } 21 | }; 22 | this.add(colorPreview); 23 | 24 | GUITextBox selectColorTxt = new GUITextBox(1, 18, w - 2, 10) { 25 | public void onEnterPress() { 26 | super.onEnterPress(); 27 | editor.selectColor(Integer.parseInt(text, 16)); 28 | } 29 | }; 30 | selectColorTxt.opaque = true; 31 | selectColorTxt.background = 0xffffff; 32 | selectColorTxt.textColor = 0x000000; 33 | selectColorTxt.nChars = 6; 34 | selectColorTxt.acceptedText = GUITextBox.HEX_ONLY; 35 | editor.selectColorTxt = selectColorTxt; 36 | this.add(selectColorTxt); 37 | 38 | int historyColumns = 4; 39 | int hHistory = 9 * (SpriteEditor.PALETTE_SIZE / historyColumns) + 1; 40 | int wHistory = 9 * historyColumns + 1; 41 | GUIPanel history = new SpriteColorbarPalette((w - wHistory) / 2, h - hHistory - 1, 42 | wHistory, hHistory, 43 | editor, historyColumns); 44 | this.add(history); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/sprite/gui/SpriteColorbarPalette.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.sprite.gui; 2 | 3 | import vulc.luag.editor.sprite.SpriteEditor; 4 | import vulc.luag.gfx.Colors; 5 | import vulc.luag.gfx.Screen; 6 | import vulc.luag.gfx.gui.GUIComponent; 7 | import vulc.luag.gfx.gui.GUIPanel; 8 | 9 | public class SpriteColorbarPalette extends GUIPanel { 10 | 11 | public SpriteColorbarPalette(int x, int y, int w, int h, SpriteEditor editor, int columns) { 12 | super(x, y, w, h); 13 | 14 | this.background = Colors.BACKGROUND_1; 15 | 16 | for(int i = 0; i < SpriteEditor.PALETTE_SIZE; i++) { 17 | int id = i; 18 | int xt = (id % columns); 19 | int yt = (id / columns); 20 | 21 | GUIComponent comp = new GUIComponent(1 + xt * 9, 1 + yt * 9, 8, 8) { 22 | public void render(Screen screen) { 23 | screen.fill(x, y, x + w - 1, y + h - 1, editor.lastColors.get(id)); 24 | } 25 | 26 | public void onMousePress(int xMouse, int yMouse) { 27 | editor.selectColor(editor.lastColors.get(id)); 28 | } 29 | }; 30 | this.add(comp); 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/sprite/gui/SpritePreview.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.sprite.gui; 2 | 3 | import vulc.bitmap.Bitmap; 4 | import vulc.luag.editor.sprite.SpriteEditor; 5 | import vulc.luag.editor.sprite.tool.SpriteTool; 6 | import vulc.luag.editor.sprite.tool.SpriteToolkit; 7 | import vulc.luag.gfx.Colors; 8 | import vulc.luag.gfx.Screen; 9 | import vulc.luag.gfx.gui.GUIComponent; 10 | 11 | public class SpritePreview extends GUIComponent { 12 | 13 | private enum ToolAction { 14 | DOWN, PRESS, RELEASE 15 | } 16 | 17 | public static final int BORDER = 2; 18 | 19 | public final SpriteEditor editor; 20 | private int animationTicks = 0; 21 | 22 | public SpritePreview(int x, int y, int w, int h, SpriteEditor editor) { 23 | super(x, y, w, h); 24 | this.editor = editor; 25 | 26 | this.opaque = true; 27 | this.background = Colors.BACKGROUND_0; 28 | } 29 | 30 | public void tick() { 31 | animationTicks++; 32 | } 33 | 34 | public void render(Screen screen) { 35 | super.render(screen); 36 | Bitmap preview = editor.preview.getCopy(); 37 | 38 | if(editor.pasteMode) { 39 | preview.draw(editor.copied, editor.xPasted, editor.yPasted); 40 | } 41 | 42 | // selection highlight 43 | SpriteToolkit toolkit = editor.toolkit; 44 | if(toolkit.currentTool == toolkit.select) { 45 | int x0 = Math.min(editor.selx0, editor.selx1); 46 | int y0 = Math.min(editor.sely0, editor.sely1); 47 | int x1 = Math.max(editor.selx0, editor.selx1); 48 | int y1 = Math.max(editor.sely0, editor.sely1); 49 | 50 | int transparency = animationTicks / 50 % 2 == 0 ? 0x55 : 0x77; 51 | preview.fill(x0, y0, x1, y1, 0xffffff, transparency); 52 | } 53 | 54 | preview = preview.getScaled(SpriteEditor.DEFAULT_SCALE / editor.scope); 55 | screen.draw(preview, x + BORDER, y + BORDER); 56 | 57 | } 58 | 59 | public void onMouseDown(int xMouse, int yMouse) { 60 | onAction(xMouse, yMouse, ToolAction.DOWN); 61 | } 62 | 63 | public void onMousePress(int xMouse, int yMouse) { 64 | onAction(xMouse, yMouse, ToolAction.PRESS); 65 | } 66 | 67 | public void onMouseRelease(int xMouse, int yMouse) { 68 | onAction(xMouse, yMouse, ToolAction.RELEASE); 69 | } 70 | 71 | private void onAction(int xMouse, int yMouse, ToolAction action) { 72 | int scale = SpriteEditor.DEFAULT_SCALE / editor.scope; 73 | 74 | int xPix = Math.floorDiv(xMouse - BORDER, scale); 75 | int yPix = Math.floorDiv(yMouse - BORDER, scale); 76 | 77 | if(xPix < 0 || xPix >= editor.preview.width 78 | || yPix < 0 || yPix >= editor.preview.height) return; 79 | 80 | SpriteTool tool = editor.toolkit.currentTool; 81 | 82 | if((action == ToolAction.DOWN && tool.onMouseDown(xPix, yPix, editor, editor.preview)) 83 | || (action == ToolAction.PRESS && tool.onMousePress(xPix, yPix, editor, editor.preview)) 84 | || (action == ToolAction.RELEASE && tool.onMouseRelease(xPix, yPix, editor, editor.preview))) { 85 | editor.isEditing = true; 86 | editor.shouldSaveContent = true; 87 | } else if(editor.wasEditing) { 88 | editor.isEditing = true; 89 | } 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/sprite/gui/SpriteScopeButton.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.sprite.gui; 2 | 3 | import vulc.luag.editor.sprite.SpriteEditor; 4 | import vulc.luag.gfx.Colors; 5 | import vulc.luag.gfx.Icons; 6 | import vulc.luag.gfx.gui.GUIButton; 7 | 8 | public class SpriteScopeButton extends GUIButton { 9 | 10 | public SpriteScopeButton(int x, int y, int w, int h, SpriteEditor editor, int scope) { 11 | super(x, y, w, h); 12 | background = Colors.BACKGROUND_1; 13 | colorAsBool = Colors.FOREGROUND_1; 14 | opaque = true; 15 | 16 | if(scope == 1) { 17 | boolImage = Icons.SPRITE_SCOPE_1; 18 | } else if(scope == 2) { 19 | boolImage = Icons.SPRITE_SCOPE_2; 20 | } 21 | 22 | this.onMousePressAction = () -> { 23 | if(editor.scope != scope) { 24 | editor.setScope(scope); 25 | } 26 | }; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/sprite/gui/SpriteScopeSelector.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.sprite.gui; 2 | 3 | import vulc.luag.editor.sprite.SpriteEditor; 4 | import vulc.luag.gfx.Colors; 5 | import vulc.luag.gfx.gui.GUIPanel; 6 | 7 | public class SpriteScopeSelector extends GUIPanel { 8 | 9 | public SpriteScopeSelector(int x, int y, int w, int h, SpriteEditor editor, int[] scopes) { 10 | super(x, y, w, h); 11 | background = Colors.BACKGROUND_0; 12 | 13 | for(int i = 0; i < scopes.length; i++) { 14 | add(new SpriteScopeButton(1, 1 + i * 9, 8, 8, editor, scopes[i])); 15 | } 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/sprite/gui/SpriteToolButton.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.sprite.gui; 2 | 3 | import vulc.luag.editor.sprite.SpriteEditor; 4 | import vulc.luag.editor.sprite.tool.SpriteTool; 5 | import vulc.luag.editor.sprite.tool.SpriteToolkit; 6 | import vulc.luag.gfx.gui.GUIButton; 7 | 8 | public class SpriteToolButton extends GUIButton { 9 | 10 | public final SpriteTool tool; 11 | 12 | public SpriteToolButton(int x, int y, int w, int h, SpriteEditor editor, SpriteTool tool) { 13 | super(x, y, w, h); 14 | 15 | this.tool = tool; 16 | 17 | SpriteToolkit toolkit = editor.toolkit; 18 | toolkit.buttons.add(this); 19 | 20 | this.onMousePressAction = () -> { 21 | toolkit.setTool(tool); 22 | }; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/sprite/gui/SpriteToolbar.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.sprite.gui; 2 | 3 | import vulc.luag.editor.sprite.SpriteEditor; 4 | import vulc.luag.editor.sprite.tool.SpriteTool; 5 | import vulc.luag.editor.sprite.tool.SpriteToolkit; 6 | import vulc.luag.gfx.Colors; 7 | import vulc.luag.gfx.Icons; 8 | import vulc.luag.gfx.gui.GUIButton; 9 | import vulc.luag.gfx.gui.GUIPanel; 10 | 11 | public class SpriteToolbar extends GUIPanel { 12 | 13 | public SpriteToolbar(int x, int y, int w, int h, SpriteEditor editor) { 14 | super(x, y, w, h); 15 | 16 | this.background = Colors.BACKGROUND_0; 17 | 18 | SpriteToolkit toolkit = editor.toolkit; 19 | 20 | SpriteToolButton pencilButton = createToolButton(0, 0, editor, toolkit.pencil); 21 | pencilButton.setImage(Icons.PENCIL_TOOL, Colors.FOREGROUND_1); 22 | this.add(pencilButton); 23 | 24 | SpriteToolButton bucketButton = createToolButton(0, 1, editor, toolkit.bucket); 25 | bucketButton.setImage(Icons.BUCKET_TOOL, Colors.FOREGROUND_1); 26 | this.add(bucketButton); 27 | 28 | SpriteToolButton pickupButton = createToolButton(0, 2, editor, toolkit.pickup); 29 | pickupButton.setImage(Icons.PICKUP_TOOL, Colors.FOREGROUND_1); 30 | this.add(pickupButton); 31 | 32 | GUIButton undoButton = createGUIButton(0, 3); 33 | undoButton.setImage(Icons.UNDO, Colors.FOREGROUND_1); 34 | undoButton.onMousePressAction = () -> { 35 | editor.undo(); 36 | }; 37 | this.add(undoButton); 38 | 39 | GUIButton redoButton = createGUIButton(0, 4); 40 | redoButton.setImage(Icons.REDO, Colors.FOREGROUND_1); 41 | redoButton.onMousePressAction = () -> { 42 | editor.redo(); 43 | }; 44 | this.add(redoButton); 45 | 46 | SpriteToolButton selectButton = createToolButton(1, 0, editor, toolkit.select); 47 | selectButton.setImage(Icons.SELECT_TOOL, Colors.FOREGROUND_1); 48 | this.add(selectButton); 49 | } 50 | 51 | private static SpriteToolButton createToolButton(int x, int y, SpriteEditor editor, SpriteTool tool) { 52 | int[] b = getButtonBounds(x, y); 53 | return (SpriteToolButton) setAttributes(new SpriteToolButton(b[0], b[1], b[2], b[3], editor, tool)); 54 | } 55 | 56 | private static GUIButton createGUIButton(int x, int y) { 57 | int[] b = getButtonBounds(x, y); 58 | return setAttributes(new GUIButton(b[0], b[1], b[2], b[3])); 59 | } 60 | 61 | private static int[] getButtonBounds(int x, int y) { 62 | int[] result = new int[4]; 63 | result[0] = 1 + 9 * x; 64 | result[1] = 1 + 9 * y; 65 | result[2] = 8; 66 | result[3] = 8; 67 | return result; 68 | } 69 | 70 | private static GUIButton setAttributes(GUIButton button) { 71 | button.opaque = true; 72 | button.background = Colors.BACKGROUND_1; 73 | return button; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/sprite/history/History.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.sprite.history; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import vulc.bitmap.Bitmap; 7 | import vulc.luag.editor.sprite.SpriteEditor; 8 | 9 | public class History { 10 | 11 | private final SpriteEditor editor; 12 | private final int historySize; 13 | 14 | private List> records = new ArrayList>(); 15 | private int nextHistoryIndex = 0; 16 | 17 | public History(SpriteEditor editor, int size) { 18 | this.editor = editor; 19 | this.historySize = size; 20 | } 21 | 22 | public void save() { 23 | // if UNDOs where done, clear the "future" records 24 | records = records.subList(0, nextHistoryIndex); 25 | 26 | records.add(editor.atlas.getCopy()); 27 | if(records.size() > historySize) { 28 | records.remove(0); 29 | } 30 | nextHistoryIndex = records.size(); 31 | } 32 | 33 | public boolean undo() { 34 | if(nextHistoryIndex == 1) return false; 35 | 36 | editor.atlas.draw(records.get(nextHistoryIndex - 2), 0, 0); 37 | nextHistoryIndex--; 38 | 39 | return true; 40 | } 41 | 42 | public boolean redo() { 43 | if(nextHistoryIndex == records.size()) return false; 44 | 45 | editor.atlas.draw(records.get(nextHistoryIndex), 0, 0); 46 | nextHistoryIndex++; 47 | 48 | return true; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/sprite/tool/BucketTool.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.sprite.tool; 2 | 3 | import vulc.bitmap.Bitmap; 4 | import vulc.luag.editor.sprite.SpriteEditor; 5 | 6 | public class BucketTool extends SpriteTool { 7 | 8 | public boolean onMouseDown(int x, int y, SpriteEditor editor, Bitmap canvas) { 9 | boolean[][] checked = new boolean[canvas.width][canvas.height]; 10 | 11 | int newColor = editor.selectedColor; 12 | int backgroundColor = canvas.getPixel(x, y); 13 | 14 | if(newColor == backgroundColor) return false; 15 | 16 | fill(x, y, canvas, backgroundColor, newColor, checked); 17 | return true; 18 | } 19 | 20 | private void fill(int x, int y, Bitmap canvas, int backgroundColor, int newColor, boolean[][] checked) { 21 | if(x < 0 || y < 0 || x >= canvas.width || y >= canvas.height) return; 22 | if(checked[x][y]) return; 23 | 24 | checked[x][y] = true; 25 | if(canvas.getPixel(x, y) == backgroundColor) { 26 | canvas.setPixel(x, y, newColor); 27 | 28 | fill(x - 1, y, canvas, backgroundColor, newColor, checked); 29 | fill(x + 1, y, canvas, backgroundColor, newColor, checked); 30 | fill(x, y - 1, canvas, backgroundColor, newColor, checked); 31 | fill(x, y + 1, canvas, backgroundColor, newColor, checked); 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/sprite/tool/PencilTool.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.sprite.tool; 2 | 3 | import vulc.bitmap.Bitmap; 4 | import vulc.luag.editor.sprite.SpriteEditor; 5 | 6 | public class PencilTool extends SpriteTool { 7 | 8 | public boolean onMouseDown(int x, int y, SpriteEditor editor, Bitmap canvas) { 9 | int color = editor.selectedColor; 10 | if(canvas.getPixel(x, y) != color) { 11 | canvas.setPixel(x, y, color); 12 | return true; 13 | } 14 | return false; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/sprite/tool/PickupTool.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.sprite.tool; 2 | 3 | import vulc.bitmap.Bitmap; 4 | import vulc.luag.editor.sprite.SpriteEditor; 5 | 6 | public class PickupTool extends SpriteTool { 7 | 8 | public boolean onMouseDown(int x, int y, SpriteEditor editor, Bitmap canvas) { 9 | editor.selectColor(canvas.getPixel(x, y)); 10 | 11 | SpriteToolkit toolkit = editor.toolkit; 12 | toolkit.setTool(toolkit.pencil); 13 | 14 | return false; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/sprite/tool/SelectTool.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.sprite.tool; 2 | 3 | import vulc.bitmap.Bitmap; 4 | import vulc.luag.editor.sprite.SpriteEditor; 5 | 6 | public class SelectTool extends SpriteTool { 7 | 8 | public boolean onMousePress(int x, int y, SpriteEditor editor, Bitmap canvas) { 9 | editor.selx0 = x; 10 | editor.sely0 = y; 11 | 12 | if(editor.pasteMode) editor.endPaste(); 13 | return false; 14 | } 15 | 16 | public boolean onMouseDown(int x, int y, SpriteEditor editor, Bitmap canvas) { 17 | editor.selx1 = x; 18 | editor.sely1 = y; 19 | 20 | if(editor.pasteMode) editor.endPaste(); 21 | return false; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/sprite/tool/SpriteTool.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.sprite.tool; 2 | 3 | import vulc.bitmap.Bitmap; 4 | import vulc.luag.editor.sprite.SpriteEditor; 5 | 6 | public abstract class SpriteTool { 7 | 8 | // returns true if is editing 9 | public boolean onMouseDown(int x, int y, SpriteEditor editor, Bitmap canvas) { 10 | return false; 11 | } 12 | 13 | public boolean onMousePress(int x, int y, SpriteEditor editor, Bitmap canvas) { 14 | return false; 15 | } 16 | 17 | public boolean onMouseRelease(int x, int y, SpriteEditor editor, Bitmap canvas) { 18 | return false; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/vulc/luag/editor/sprite/tool/SpriteToolkit.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.editor.sprite.tool; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import vulc.luag.editor.sprite.gui.SpriteToolButton; 7 | import vulc.luag.gfx.Colors; 8 | 9 | public class SpriteToolkit { 10 | 11 | public final List buttons = new ArrayList(); 12 | 13 | public final SpriteTool pencil = new PencilTool(); 14 | public final SpriteTool bucket = new BucketTool(); 15 | public final SpriteTool pickup = new PickupTool(); 16 | public final SpriteTool select = new SelectTool(); 17 | 18 | public SpriteTool currentTool; 19 | 20 | public void setTool(SpriteTool tool) { 21 | currentTool = tool; 22 | 23 | for(int i = 0; i < buttons.size(); i++) { 24 | SpriteToolButton button = buttons.get(i); 25 | 26 | if(button.tool == tool) { 27 | button.colorAsBool = Colors.FOREGROUND_HIGHLIGHT; 28 | } else { 29 | button.colorAsBool = Colors.FOREGROUND_1; 30 | } 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/vulc/luag/game/Game.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.game; 2 | 3 | import java.awt.event.KeyEvent; 4 | import java.awt.image.BufferedImage; 5 | import java.io.BufferedInputStream; 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | import java.io.FileNotFoundException; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.io.InputStreamReader; 12 | import java.nio.file.NoSuchFileException; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.zip.ZipEntry; 16 | import java.util.zip.ZipFile; 17 | import java.util.zip.ZipInputStream; 18 | 19 | import javax.imageio.ImageIO; 20 | import javax.swing.KeyStroke; 21 | 22 | import com.google.gson.JsonArray; 23 | import com.google.gson.JsonElement; 24 | import com.google.gson.JsonObject; 25 | import com.google.gson.JsonParseException; 26 | import com.google.gson.JsonParser; 27 | 28 | import vulc.bitmap.Bitmap; 29 | import vulc.bitmap.IntBitmap; 30 | import vulc.luag.Console; 31 | import vulc.luag.Console.Mode; 32 | import vulc.luag.editor.map.MapCompiler; 33 | import vulc.luag.game.map.Map; 34 | import vulc.luag.game.save.SaveSystem; 35 | import vulc.luag.gfx.panel.GamePanel; 36 | import vulc.luag.gfx.panel.ShellPanel; 37 | import vulc.luag.input.InputHandler; 38 | import vulc.luag.input.InputHandler.Key; 39 | import vulc.luag.input.InputHandler.KeyType; 40 | 41 | public class Game { 42 | 43 | public static final String USERDATA_DIR_NAME = "console-userdata"; 44 | public static final String SCRIPT_DIR_NAME = "script"; 45 | public static final String SFX_DIR_NAME = "sfx"; 46 | public static final String CONFIG_FILE_NAME = "config.json"; 47 | public static final String ATLAS_FILE_NAME = "atlas.png"; 48 | public static final String MAP_FILE_NAME = "map"; 49 | public static final String CARTRIDGE_INFO_NAME = ".cartridge-info"; 50 | 51 | public static final String USERDATA_DIR = Console.rootDirectory + USERDATA_DIR_NAME; 52 | public static final String SCRIPT_DIR = USERDATA_DIR + "/" + SCRIPT_DIR_NAME; 53 | public static final String SFX_DIR = USERDATA_DIR + "/" + SFX_DIR_NAME; 54 | public static final String CONFIG_FILE = USERDATA_DIR + "/" + CONFIG_FILE_NAME; 55 | public static final String ATLAS_FILE = USERDATA_DIR + "/" + ATLAS_FILE_NAME; 56 | public static final String MAP_FILE = USERDATA_DIR + "/" + MAP_FILE_NAME; 57 | 58 | public static final int SPR_SIZE = 8; 59 | 60 | private final LuaScriptCore scriptCore = new LuaScriptCore(); 61 | private final InputHandler input = new InputHandler(); 62 | public final GameSounds sounds = new GameSounds(); 63 | 64 | public JsonObject jsonConfig, cartridgeInfo; 65 | public SaveSystem saveSystem; 66 | public Bitmap atlas; 67 | public Map map; 68 | 69 | public ZipFile cartridgeFile; 70 | 71 | public final List keys = new ArrayList(); 72 | 73 | private Key debugRestartGame = null; 74 | private Key debugGotoShell = null; 75 | 76 | public Game() { 77 | if(Console.mode == Mode.DEVELOPER) { 78 | debugRestartGame = input.new Key(KeyType.KEYBOARD, KeyEvent.VK_F7); 79 | debugGotoShell = input.new Key(KeyType.KEYBOARD, KeyEvent.VK_F8); 80 | } 81 | } 82 | 83 | // init resources in console-userdata 84 | public boolean initDevResources() { 85 | Console.LOGGER.info("Loading '" + USERDATA_DIR_NAME + "' resources..."); 86 | saveSystem = new SaveSystem(USERDATA_DIR); 87 | 88 | // root 89 | File rootFolder = new File(USERDATA_DIR); 90 | if(!rootFolder.isDirectory()) { 91 | Console.die("Error:\n" 92 | + "'" + USERDATA_DIR_NAME + "'\n" 93 | + "folder not found"); 94 | return false; 95 | } 96 | 97 | // sounds 98 | Console.LOGGER.info("Load '" + SFX_DIR_NAME + "'"); 99 | if(!sounds.init()) return false; 100 | 101 | // config.json 102 | Console.LOGGER.info("Load '" + CONFIG_FILE_NAME + "'"); 103 | try(InputStream in = new BufferedInputStream(new FileInputStream(CONFIG_FILE))) { 104 | if(!loadConfig(in)) return false; 105 | } catch(FileNotFoundException e) { 106 | Console.die("Error:\n" 107 | + "'" + Game.CONFIG_FILE_NAME + "'\n" 108 | + "file not found"); 109 | return false; 110 | } catch(IOException e) { 111 | e.printStackTrace(); 112 | } 113 | 114 | // atlas.png 115 | Console.LOGGER.info("Load '" + ATLAS_FILE_NAME + "'"); 116 | try(InputStream in = new BufferedInputStream(new FileInputStream(ATLAS_FILE))) { 117 | if(!loadAtlas(in)) return false; 118 | } catch(FileNotFoundException e) { 119 | Console.die("Error:\n" 120 | + "'" + Game.ATLAS_FILE_NAME + "'\n" 121 | + "file not found"); 122 | return false; 123 | } catch(IOException e) { 124 | e.printStackTrace(); 125 | } 126 | 127 | // map 128 | Console.LOGGER.info("Load '" + MAP_FILE_NAME + "'"); 129 | File mapFile = new File(MAP_FILE); 130 | try { 131 | map = Map.load(new BufferedInputStream(new FileInputStream(mapFile))); 132 | if(map == null) return false; 133 | } catch(FileNotFoundException e) { 134 | Console.LOGGER.info("Missing map file: generating new file"); 135 | 136 | map = new Map(10, 10); 137 | MapCompiler.compile(map); 138 | } 139 | return true; 140 | } 141 | 142 | public boolean initCartridgeResources() { 143 | try { 144 | String cartridge = Console.cartridge; 145 | Console.LOGGER.info("Loading cartridge '" + cartridge + "' resources..."); 146 | saveSystem = new SaveSystem(cartridge); 147 | 148 | try { 149 | cartridgeFile = new ZipFile(cartridge); 150 | } catch(FileNotFoundException | NoSuchFileException e) { 151 | Console.die("Error:\n" 152 | + "'" + cartridge + "'\n" 153 | + "cartridge not found"); 154 | return false; 155 | } 156 | List entries = new ArrayList(); 157 | { 158 | ZipInputStream zipIn = new ZipInputStream(new BufferedInputStream(new FileInputStream(cartridge))); 159 | while(true) { 160 | ZipEntry entry = zipIn.getNextEntry(); 161 | if(entry != null) { 162 | entries.add(entry); 163 | } else { 164 | break; 165 | } 166 | } 167 | zipIn.close(); 168 | } 169 | 170 | // sound 171 | Console.LOGGER.info("Load '" + SFX_DIR_NAME + "'"); 172 | if(!sounds.initAsCartridge(cartridgeFile, entries)) return false; 173 | 174 | // config.json 175 | Console.LOGGER.info("Load '" + CONFIG_FILE_NAME + "'"); 176 | ZipEntry configEntry = cartridgeFile.getEntry(CONFIG_FILE_NAME); 177 | if(configEntry == null) { 178 | Console.die("Cartridge Error:\n" 179 | + "'" + CONFIG_FILE_NAME + "'\n" 180 | + "file not found"); 181 | return false; 182 | } else { 183 | if(!loadConfig(cartridgeFile.getInputStream(configEntry))) { 184 | return false; 185 | } 186 | } 187 | 188 | // atlas.png 189 | Console.LOGGER.info("Load '" + ATLAS_FILE_NAME + "'"); 190 | ZipEntry atlasEntry = cartridgeFile.getEntry(ATLAS_FILE_NAME); 191 | if(atlasEntry == null) { 192 | Console.die("Cartridge Error:\n" 193 | + "'" + ATLAS_FILE_NAME + "'\n" 194 | + "file not found"); 195 | return false; 196 | } else { 197 | if(!loadAtlas(cartridgeFile.getInputStream(atlasEntry))) { 198 | return false; 199 | } 200 | } 201 | 202 | // map 203 | Console.LOGGER.info("Load '" + MAP_FILE_NAME + "'"); 204 | ZipEntry mapEntry = cartridgeFile.getEntry(MAP_FILE_NAME); 205 | if(mapEntry == null) { 206 | Console.die("Cartridge Error:\n" 207 | + "'" + MAP_FILE_NAME + "'\n" 208 | + "file not found"); 209 | return false; 210 | } else { 211 | this.map = Map.load(cartridgeFile.getInputStream(mapEntry)); 212 | if(map == null) return false; 213 | } 214 | 215 | // .cartridge-info 216 | Console.LOGGER.info("Load '" + CARTRIDGE_INFO_NAME + "'"); 217 | ZipEntry cartridgeInfoEntry = cartridgeFile.getEntry(CARTRIDGE_INFO_NAME); 218 | if(cartridgeInfoEntry == null) { 219 | Console.die("Cartridge Error:\n" 220 | + "'" + CARTRIDGE_INFO_NAME + "'\n" 221 | + "file not found"); 222 | return false; 223 | } else { 224 | if(!loadCartridgeInfo(cartridgeFile.getInputStream(cartridgeInfoEntry))) { 225 | return false; 226 | } 227 | } 228 | } catch(Exception e) { 229 | e.printStackTrace(); 230 | return false; 231 | } 232 | return true; 233 | } 234 | 235 | public boolean initScript() { 236 | Console.LOGGER.info("Loading scripts"); 237 | 238 | JsonElement keysElement = jsonConfig.get("keys"); 239 | if(keysElement == null || !keysElement.isJsonArray()) { 240 | Console.die("Error:\n" 241 | + "'" + CONFIG_FILE_NAME + "'\n" 242 | + "must contain\n" 243 | + "a string array 'keys'"); 244 | return false; 245 | } 246 | 247 | JsonArray keyArray = keysElement.getAsJsonArray(); 248 | for(int i = 0; i < keyArray.size(); i++) { 249 | try { 250 | String key = keyArray.get(i).getAsString().toUpperCase(); 251 | keys.add(input.new Key(KeyType.KEYBOARD, 252 | KeyStroke.getKeyStroke(key).getKeyCode())); 253 | } catch(Exception e) { 254 | Console.die("Error:\n" 255 | + "'" + CONFIG_FILE_NAME + "'\n" 256 | + "contains invalid keys"); 257 | return false; 258 | } 259 | } 260 | 261 | input.init(); 262 | if(scriptCore.init(this)) { 263 | Console.LOGGER.info("Game Started"); 264 | } 265 | return true; 266 | } 267 | 268 | private boolean loadConfig(InputStream in) { 269 | jsonConfig = loadJson(in); 270 | if(jsonConfig == null) { 271 | Console.die("Error:\n" 272 | + "'" + Game.CONFIG_FILE_NAME + "'\n" 273 | + "must be a json object"); 274 | return false; 275 | } 276 | return true; 277 | } 278 | 279 | private boolean loadCartridgeInfo(InputStream in) { 280 | cartridgeInfo = loadJson(in); 281 | if(cartridgeInfo == null) { 282 | Console.die("Error:\n" 283 | + "'" + Game.CARTRIDGE_INFO_NAME + "'\n" 284 | + "is invalid"); 285 | return false; 286 | } 287 | return true; 288 | } 289 | 290 | private boolean loadAtlas(InputStream in) { 291 | try { 292 | BufferedImage img = ImageIO.read(in); 293 | if(img == null) { 294 | Console.die("Error:\n" 295 | + "'" + Game.ATLAS_FILE_NAME + "'\n" 296 | + "is not an image"); 297 | return false; 298 | } 299 | atlas = new IntBitmap(img); 300 | if(atlas.width != 128 || atlas.height != 128) { 301 | Console.die("Error:\n" 302 | + "atlas must be\n" 303 | + "128x128 pixels\n" 304 | + "(256 sprites)"); 305 | return false; 306 | } 307 | } catch(IOException e) { 308 | e.printStackTrace(); 309 | } 310 | return true; 311 | } 312 | 313 | private JsonObject loadJson(InputStream in) { 314 | try { 315 | JsonElement element = new JsonParser().parse(new InputStreamReader(in)); 316 | if(!element.isJsonObject()) return null; 317 | return element.getAsJsonObject(); 318 | } catch(JsonParseException e) { 319 | return null; 320 | } 321 | } 322 | 323 | public void tick() { 324 | input.tick(); 325 | 326 | scriptCore.tick(); 327 | saveSystem.tick(); 328 | 329 | if(Console.mode == Mode.DEVELOPER) { 330 | if(debugRestartGame.isPressed()) { 331 | Console.LOGGER.info("Restarting Game"); 332 | Console.switchToPanel(new GamePanel()); 333 | } else if(debugGotoShell.isPressed()) { 334 | Console.LOGGER.info("Stopping Game"); 335 | Console.switchToPanel(new ShellPanel()); 336 | } 337 | } 338 | } 339 | 340 | public void remove() { 341 | input.remove(); 342 | sounds.remove(); 343 | 344 | if(cartridgeFile != null) { 345 | try { 346 | cartridgeFile.close(); 347 | } catch(IOException e) { 348 | e.printStackTrace(); 349 | } 350 | cartridgeFile = null; 351 | } 352 | } 353 | 354 | public Bitmap getSprite(int id, int sw, int sh) { 355 | return getSprite(id % 16, id / 16, sw, sh); 356 | } 357 | 358 | public Bitmap getSprite(int x, int y, int sw, int sh) { 359 | return atlas.getSubimage(x * SPR_SIZE, y * SPR_SIZE, sw * SPR_SIZE, sh * SPR_SIZE); 360 | } 361 | 362 | } 363 | -------------------------------------------------------------------------------- /src/vulc/luag/game/GameSounds.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.game; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.IOException; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Set; 10 | import java.util.zip.ZipEntry; 11 | import java.util.zip.ZipFile; 12 | 13 | import vulc.luag.Console; 14 | import vulc.luag.sfx.Sound; 15 | 16 | public class GameSounds { 17 | 18 | private final HashMap list = new HashMap(); 19 | 20 | public Sound get(String name) { 21 | return list.get(name); 22 | } 23 | 24 | public void remove() { 25 | Set keys = list.keySet(); 26 | for(String key : keys) { 27 | list.get(key).onRemove(); 28 | } 29 | list.clear(); 30 | } 31 | 32 | public boolean init() { 33 | File sfxDir = new File(Game.SFX_DIR); 34 | if(!sfxDir.isDirectory()) { 35 | Console.die("Error:\n" 36 | + "'" + Game.SFX_DIR_NAME + "'\n" 37 | + "folder not found"); 38 | return false; 39 | } 40 | readSoundsInFolder(sfxDir, ""); 41 | return true; 42 | } 43 | 44 | private void readSoundsInFolder(File folder, String relativeToSfxRoot) { 45 | File[] files = folder.listFiles(); 46 | for(File file : files) { 47 | String fileName = file.getName(); 48 | 49 | if(file.isDirectory()) { 50 | readSoundsInFolder(file, relativeToSfxRoot + fileName + "/"); 51 | } else { 52 | if(fileName.endsWith(".wav")) { 53 | String name = relativeToSfxRoot + fileName; 54 | name = name.substring(0, name.lastIndexOf('.')); 55 | try { 56 | list.put(name, 57 | new Sound(new BufferedInputStream(new FileInputStream(file)))); 58 | } catch(IOException e) { 59 | e.printStackTrace(); 60 | } 61 | } 62 | } 63 | } 64 | } 65 | 66 | public boolean initAsCartridge(ZipFile zipFile, List entries) { 67 | for(ZipEntry entry : entries) { 68 | String fileName = entry.getName(); 69 | 70 | if(!fileName.startsWith(Game.SFX_DIR_NAME) 71 | || !fileName.endsWith(".wav")) continue; 72 | 73 | String sfxName = fileName.substring(fileName.indexOf("/") + 1, fileName.lastIndexOf('.')); 74 | try { 75 | list.put(sfxName, new Sound(new BufferedInputStream(zipFile.getInputStream(entry)))); 76 | } catch(IOException e) { 77 | e.printStackTrace(); 78 | } 79 | } 80 | return true; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/vulc/luag/game/LuaScriptCore.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.game; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.util.zip.ZipEntry; 9 | 10 | import org.luaj.vm2.Globals; 11 | import org.luaj.vm2.LoadState; 12 | import org.luaj.vm2.LuaError; 13 | import org.luaj.vm2.LuaFunction; 14 | import org.luaj.vm2.LuaValue; 15 | import org.luaj.vm2.compiler.LuaC; 16 | import org.luaj.vm2.lib.Bit32Lib; 17 | import org.luaj.vm2.lib.PackageLib; 18 | import org.luaj.vm2.lib.StringLib; 19 | import org.luaj.vm2.lib.TableLib; 20 | import org.luaj.vm2.lib.jse.JseBaseLib; 21 | import org.luaj.vm2.lib.jse.JseMathLib; 22 | 23 | import vulc.luag.Console; 24 | import vulc.luag.game.interfaces.LuaInterface; 25 | 26 | public class LuaScriptCore { 27 | 28 | private LuaFunction luaTick; 29 | 30 | // return true if init was successfully, else false 31 | public boolean init(Game game) { 32 | Globals globals = new Globals(); 33 | 34 | // must load 35 | globals.load(new JseBaseLib()); 36 | globals.load(new PackageLib()); 37 | 38 | // math 39 | globals.load(new Bit32Lib()); 40 | globals.load(new JseMathLib()); 41 | 42 | // utils 43 | globals.load(new TableLib()); 44 | globals.load(new StringLib()); 45 | 46 | // disable require 47 | globals.set("require", LuaValue.NIL); 48 | 49 | /* libraries that are not loaded 50 | * 51 | * CoroutineLib 52 | * JseIoLib 53 | * JseOsLib 54 | * LuajavaLib 55 | */ 56 | 57 | LoadState.install(globals); 58 | LuaC.install(globals); 59 | 60 | // load luag interface 61 | LuaInterface luaInterface; 62 | if(Console.cartridge != null) { 63 | String[] requestedVersion; 64 | int majorVerReq, minorVerReq; 65 | try { 66 | requestedVersion = game.cartridgeInfo.get("interface-version") 67 | .getAsString() 68 | .split("\\."); 69 | 70 | majorVerReq = Integer.parseInt(requestedVersion[0]); 71 | minorVerReq = Integer.parseInt(requestedVersion[1]); 72 | } catch(Exception e) { 73 | Console.die("Error:\n" 74 | + "'" + Game.CARTRIDGE_INFO_NAME + "'\n" 75 | + "is invalid"); 76 | return false; 77 | } 78 | 79 | luaInterface = LuaInterface.getInterface(majorVerReq, game, globals); 80 | if(luaInterface == null) { 81 | Console.die("Error:\n" 82 | + "interface version " + majorVerReq + "\n" 83 | + "not supported"); 84 | return false; 85 | } 86 | int yVer = LuaInterface.minorVersion(majorVerReq); 87 | if(yVer < minorVerReq) { 88 | Console.die("Error:\n" 89 | + "interface " + majorVerReq + "." + minorVerReq + "\n" 90 | + "not supported\n" 91 | + "Lastest: " + majorVerReq + "." + LuaInterface.minorVersion(majorVerReq)); 92 | return false; 93 | } 94 | } else { 95 | luaInterface = LuaInterface.getInterface(LuaInterface.DEFAULT_MAJOR_VERSION, game, globals); 96 | } 97 | luaInterface.init(); 98 | 99 | // READ main.lua 100 | boolean error = false; 101 | InputStream input = null; 102 | try { 103 | Console.LOGGER.info("Load 'main.lua'"); 104 | if(Console.cartridge == null) { 105 | File mainFile = new File(Game.SCRIPT_DIR + "/main.lua"); 106 | if(!mainFile.exists()) { 107 | Console.die("Error:\n" 108 | + "'" + Game.SCRIPT_DIR_NAME + "/main.lua'\n" 109 | + "file not found"); 110 | return false; 111 | } 112 | 113 | input = new BufferedInputStream(new FileInputStream(mainFile)); 114 | } else { 115 | ZipEntry mainLuaEntry = game.cartridgeFile.getEntry("script/main.lua"); 116 | if(mainLuaEntry == null || mainLuaEntry.isDirectory()) { 117 | Console.die("Cartridge Error:\n" 118 | + "'" + Game.SCRIPT_DIR_NAME + "/main.lua'\n" 119 | + "file not found"); 120 | return false; 121 | } 122 | 123 | input = game.cartridgeFile.getInputStream(mainLuaEntry); 124 | } 125 | 126 | globals.load(input, "@main.lua", "t", globals).call(); 127 | } catch(LuaError e) { 128 | handleError(e); 129 | error = true; 130 | } catch(IOException e) { 131 | e.printStackTrace(); 132 | error = true; 133 | } 134 | 135 | try { 136 | input.close(); 137 | } catch(IOException e) { 138 | e.printStackTrace(); 139 | } 140 | if(error) return false; 141 | 142 | try { 143 | // load tick 144 | LuaValue tick = globals.get("tick"); 145 | if(tick == LuaValue.NIL || !tick.isfunction()) { 146 | Console.die("Error:\n" 147 | + "'main.lua' must contain\n" 148 | + "a function 'tick()'"); 149 | return false; 150 | } else { 151 | luaTick = tick.checkfunction(); 152 | } 153 | 154 | // call init 155 | Console.LOGGER.info("Calling 'init' function"); 156 | LuaValue init = globals.get("init"); 157 | if(init == LuaValue.NIL || !init.isfunction()) { 158 | Console.die("Error:\n" 159 | + "'main.lua' must contain\n" 160 | + "a function 'init()'"); 161 | return false; 162 | } else { 163 | init.checkfunction().call(); 164 | } 165 | } catch(LuaError e) { 166 | handleError(e); 167 | return false; 168 | } 169 | return true; 170 | } 171 | 172 | public void tick() { 173 | try { 174 | luaTick.call(); 175 | } catch(LuaError e) { 176 | handleError(e); 177 | } 178 | } 179 | 180 | private void handleError(LuaError e) { 181 | String sep = "[\\\\/]"; 182 | 183 | String msg = "LuaError: " 184 | + e.getLocalizedMessage() 185 | .replace("@", "") 186 | .replaceAll("\\.?" + sep + Game.USERDATA_DIR_NAME, ""); 187 | 188 | Console.LOGGER.severe(msg); 189 | 190 | Console.die("Script Error:\n" 191 | + "see the log file"); 192 | } 193 | 194 | } 195 | -------------------------------------------------------------------------------- /src/vulc/luag/game/cartridge/Cartridge.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.game.cartridge; 2 | 3 | public abstract class Cartridge { 4 | 5 | public static final String EXTENSION = "luag"; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/vulc/luag/game/interfaces/Interface001.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.game.interfaces; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.util.zip.ZipEntry; 9 | import java.util.zip.ZipFile; 10 | 11 | import org.luaj.vm2.Globals; 12 | import org.luaj.vm2.LuaError; 13 | import org.luaj.vm2.LuaValue; 14 | import org.luaj.vm2.Varargs; 15 | import org.luaj.vm2.lib.OneArgFunction; 16 | import org.luaj.vm2.lib.ThreeArgFunction; 17 | import org.luaj.vm2.lib.TwoArgFunction; 18 | import org.luaj.vm2.lib.VarArgFunction; 19 | 20 | import vulc.luag.Console; 21 | import vulc.luag.game.Game; 22 | import vulc.luag.gfx.Screen; 23 | import vulc.luag.sfx.Sound; 24 | 25 | public class Interface001 extends LuaInterface { 26 | 27 | public Interface001(Game game, Globals env) { 28 | super(game, env); 29 | } 30 | 31 | public void init() { 32 | // SCREEN 33 | env.set("scr_w", Console.SCREEN.width); 34 | env.set("scr_h", Console.SCREEN.height); 35 | 36 | // FONT 37 | env.set("font_w", Screen.FONT.widthOf(' ')); 38 | env.set("font_h", Screen.FONT.getHeight()); 39 | 40 | // MAP 41 | env.set("map_w", game.map.width); 42 | env.set("map_h", game.map.height); 43 | 44 | // FUNCTIONS 45 | // general 46 | env.set("loadscript", new loadscript()); 47 | env.set("log", new log()); 48 | 49 | // keys 50 | env.set("key", new key()); 51 | env.set("key_down", new key()); 52 | env.set("key_pressed", new key_pressed()); 53 | env.set("key_released", new key_released()); 54 | 55 | // sound 56 | env.set("sfx", new sfx()); 57 | env.set("sfx_play", new sfx()); 58 | env.set("sfx_loop", new sfx_loop()); 59 | env.set("sfx_stop", new sfx_stop()); 60 | 61 | // screen 62 | env.set("settransparent", new settransparent()); 63 | env.set("clear", new clear()); 64 | env.set("pix", new pix()); 65 | env.set("write", new write()); 66 | env.set("spr", new spr()); 67 | 68 | // map 69 | env.set("get_tile", new get_tile()); 70 | env.set("set_tile", new set_tile()); 71 | env.set("maprender", new maprender()); 72 | 73 | // save 74 | env.set("save_data", new save_data()); 75 | env.set("load_data", new load_data()); 76 | } 77 | 78 | //---GENERAL---\\ 79 | 80 | private class loadscript extends OneArgFunction { 81 | public LuaValue call(LuaValue script) { 82 | Console.LOGGER.info("Loading script '" + script + "'"); 83 | 84 | InputStream input = null; 85 | try { 86 | if(Console.cartridge == null) { 87 | File file = new File(Game.SCRIPT_DIR + "/" + script); 88 | if(!file.exists()) throw new LuaError("bad argument: file '" + script + "' not found"); 89 | 90 | input = new BufferedInputStream(new FileInputStream(file)); 91 | } else { 92 | ZipFile cartridgeFile = game.cartridgeFile; 93 | ZipEntry entry = cartridgeFile.getEntry(Game.SCRIPT_DIR_NAME 94 | + "/" + script); 95 | if(entry == null) throw new LuaError("bad argument: file '" + script + "' not found"); 96 | 97 | input = cartridgeFile.getInputStream(entry); 98 | } 99 | env.load(input, "@" + script, "t", env).call(); 100 | } catch(IOException e) { 101 | e.printStackTrace(); 102 | } 103 | 104 | try { 105 | input.close(); 106 | } catch(IOException e) { 107 | e.printStackTrace(); 108 | } 109 | return NIL; 110 | } 111 | } 112 | 113 | private class log extends OneArgFunction { 114 | public LuaValue call(LuaValue x) { 115 | Console.LOGGER.info(x.toString()); 116 | return NIL; 117 | } 118 | } 119 | 120 | //---KEYS---\\ 121 | 122 | private class key extends OneArgFunction { 123 | public LuaValue call(LuaValue id) { 124 | int key = id.checkint(); 125 | if(key < 0 || key >= game.keys.size()) throw new LuaError("bad argument: key '" + key + "' does not exist"); 126 | 127 | return valueOf(game.keys.get(key).isKeyDown()); 128 | } 129 | } 130 | 131 | private class key_pressed extends OneArgFunction { 132 | public LuaValue call(LuaValue id) { 133 | int key = id.checkint(); 134 | if(key < 0 || key >= game.keys.size()) throw new LuaError("bad argument: key '" + key + "' does not exist"); 135 | 136 | return valueOf(game.keys.get(key).isPressed()); 137 | } 138 | } 139 | 140 | private class key_released extends OneArgFunction { 141 | public LuaValue call(LuaValue id) { 142 | int key = id.checkint(); 143 | if(key < 0 || key >= game.keys.size()) throw new LuaError("bad argument: key '" + key + "' does not exist"); 144 | 145 | return valueOf(game.keys.get(key).isReleased()); 146 | } 147 | } 148 | 149 | //---SOUND---\\ 150 | 151 | private class sfx extends OneArgFunction { 152 | public LuaValue call(LuaValue name) { 153 | Sound sound = game.sounds.get(name.toString()); 154 | if(sound == null) throw new LuaError("bad argument: sound '" + name + "' does not exist"); 155 | sound.play(); 156 | 157 | return NIL; 158 | } 159 | } 160 | 161 | private class sfx_loop extends OneArgFunction { 162 | public LuaValue call(LuaValue name) { 163 | Sound sound = game.sounds.get(name.toString()); 164 | if(sound == null) throw new LuaError("bad argument: sound '" + name + "' does not exist"); 165 | sound.loop(); 166 | 167 | return NIL; 168 | } 169 | } 170 | 171 | private class sfx_stop extends OneArgFunction { 172 | public LuaValue call(LuaValue name) { 173 | Sound sound = game.sounds.get(name.toString()); 174 | if(sound == null) throw new LuaError("bad argument: sound '" + name + "' does not exist"); 175 | sound.stop(); 176 | 177 | return NIL; 178 | } 179 | } 180 | 181 | //---SCREEN---\\ 182 | 183 | private class settransparent extends OneArgFunction { 184 | public LuaValue call(LuaValue color) { 185 | Console.SCREEN.setTransparent(color.checkint()); 186 | return NIL; 187 | } 188 | } 189 | 190 | private class clear extends OneArgFunction { 191 | public LuaValue call(LuaValue color) { 192 | Console.SCREEN.clear(color.checkint()); 193 | return NIL; 194 | } 195 | } 196 | 197 | private class pix extends VarArgFunction { 198 | public Varargs invoke(Varargs args) { 199 | int x = args.arg(1).checkint(); 200 | int y = args.arg(2).checkint(); 201 | int color = args.arg(3).checkint(); 202 | int w = args.arg(4).isnil() ? 1 : args.arg(4).checkint(); 203 | int h = args.arg(5).isnil() ? 1 : args.arg(5).checkint(); 204 | 205 | Console.SCREEN.fill(x, y, x + w - 1, y + h - 1, color); 206 | return NIL; 207 | } 208 | } 209 | 210 | private class write extends VarArgFunction { 211 | public Varargs invoke(Varargs args) { 212 | String text = args.arg(1).toString(); 213 | int color = args.arg(2).checkint(); 214 | int x = args.arg(3).checkint(); 215 | int y = args.arg(4).checkint(); 216 | 217 | Console.SCREEN.write(text, color, x, y); 218 | return NIL; 219 | } 220 | } 221 | 222 | // note for next interface version: change arguments order 223 | private class spr extends VarArgFunction { 224 | public Varargs invoke(Varargs args) { 225 | int id = args.arg(1).checkint(); 226 | int x = args.arg(2).checkint(); 227 | int y = args.arg(3).checkint(); 228 | 229 | int scale = args.arg(4).isnil() ? 1 : args.arg(4).checkint(); 230 | 231 | int sw = args.arg(5).isnil() ? 1 : args.arg(5).checkint(); 232 | int sh = args.arg(6).isnil() ? 1 : args.arg(6).checkint(); 233 | 234 | int rot = args.arg(7).isnil() ? 0 : args.arg(7).checkint(); 235 | 236 | boolean hflip = args.arg(8).isnil() ? false : args.arg(8).checkboolean(); 237 | boolean vflip = args.arg(9).isnil() ? false : args.arg(9).checkboolean(); 238 | 239 | int xSprite = id % 16; 240 | int ySprite = id / 16; 241 | 242 | if(id < 0 || id >= 256) throw new LuaError("bad argument: id"); 243 | if(scale <= 0) throw new LuaError("bad argument: scale"); 244 | if(sw <= 0 || xSprite + sw > 16) throw new LuaError("bad argument: sw"); 245 | if(sh <= 0 || ySprite + sh > 16) throw new LuaError("bad argument: sh"); 246 | 247 | // flip is applied before rotation 248 | Console.SCREEN.draw(game.getSprite(xSprite, ySprite, sw, sh) 249 | .getFlipped(hflip, vflip) 250 | .getRotated(rot) 251 | .getScaled(scale), 252 | x, y); 253 | 254 | return NIL; 255 | } 256 | } 257 | 258 | //---MAP---\\ 259 | 260 | private class get_tile extends TwoArgFunction { 261 | public LuaValue call(LuaValue x, LuaValue y) { 262 | int xt = x.checkint(); 263 | int yt = y.checkint(); 264 | 265 | if(xt < 0 || xt >= game.map.width) throw new LuaError("bad argument: x"); 266 | if(yt < 0 || yt >= game.map.height) throw new LuaError("bad argument: y"); 267 | 268 | return valueOf(game.map.getTile(xt, yt)); 269 | } 270 | } 271 | 272 | // note for next interface version: change to set_tile(id, x, y) 273 | private class set_tile extends ThreeArgFunction { 274 | public LuaValue call(LuaValue x, LuaValue y, LuaValue idArg) { 275 | int xt = x.checkint(); 276 | int yt = y.checkint(); 277 | int id = idArg.checkint(); 278 | 279 | if(xt < 0 || xt >= game.map.width) throw new LuaError("bad argument: x"); 280 | if(yt < 0 || yt >= game.map.height) throw new LuaError("bad argument: y"); 281 | if(id < 0 || id >= 256) throw new LuaError("bad argument: id"); 282 | 283 | game.map.setTile(xt, yt, id); 284 | return NIL; 285 | } 286 | } 287 | 288 | // consider changing the arguments from offset to position (eg. draw on (x, y), not offset) 289 | private class maprender extends ThreeArgFunction { 290 | public LuaValue call(LuaValue scaleArg, LuaValue xOffArg, LuaValue yOffArg) { 291 | int scale = scaleArg.isnil() ? 1 : scaleArg.checkint(); 292 | int xoff = xOffArg.isnil() ? 0 : xOffArg.checkint(); 293 | int yoff = yOffArg.isnil() ? 0 : yOffArg.checkint(); 294 | 295 | if(scale <= 0) throw new LuaError("bad argument: scale"); 296 | 297 | game.map.render(Console.SCREEN, game, xoff, yoff, scale); 298 | return NIL; 299 | } 300 | } 301 | 302 | //---SAVE---\\ 303 | 304 | private class save_data extends OneArgFunction { 305 | public LuaValue call(LuaValue saveTable) { 306 | return LuaValue.valueOf(game.saveSystem.serialize(saveTable.checktable())); 307 | } 308 | } 309 | 310 | private class load_data extends OneArgFunction { 311 | public LuaValue call(LuaValue arg) { 312 | return game.saveSystem.deserialize(); 313 | } 314 | } 315 | 316 | } 317 | -------------------------------------------------------------------------------- /src/vulc/luag/game/interfaces/LuaInterface.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.game.interfaces; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import org.luaj.vm2.Globals; 7 | 8 | import vulc.luag.game.Game; 9 | 10 | /** versioning: major.minor 11 | * major -> backward compatibility with other major versions is broken 12 | * minor -> backward compatibility with the same major version is not broken 13 | * 14 | * eg. 6.4 can run using an interface 6.7, 15 | * but 6.7 cannot run using an interface 6.4 16 | */ 17 | public abstract class LuaInterface { 18 | 19 | public static final Map> INTERFACES = 20 | new HashMap>(); 21 | public static final Map MINOR_VERSIONS = new HashMap(); 22 | 23 | public static final int DEFAULT_MAJOR_VERSION = 1; 24 | 25 | static { 26 | INTERFACES.put(1, Interface001.class); 27 | MINOR_VERSIONS.put(1, 4); 28 | } 29 | 30 | protected final Game game; 31 | protected final Globals env; 32 | 33 | public LuaInterface(Game game, Globals env) { 34 | this.game = game; 35 | this.env = env; 36 | } 37 | 38 | public abstract void init(); 39 | 40 | public static LuaInterface getInterface(int majorVersion, Game game, Globals env) { 41 | try { 42 | Class interfaceClass = INTERFACES.get(majorVersion); 43 | if(interfaceClass != null) { 44 | return interfaceClass.getConstructor(Game.class, Globals.class).newInstance(game, env); 45 | } 46 | } catch(Exception e) { 47 | e.printStackTrace(); 48 | } 49 | return null; 50 | } 51 | 52 | public static int minorVersion(int majorVersion) { 53 | return MINOR_VERSIONS.get(majorVersion); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/vulc/luag/game/map/Map.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.game.map; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.DataInputStream; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | 8 | import vulc.luag.Console; 9 | import vulc.luag.game.Game; 10 | import vulc.luag.gfx.Screen; 11 | 12 | public class Map { 13 | 14 | public final int width, height; 15 | public final byte[] tiles; 16 | 17 | public Map(int w, int h) { 18 | this.width = w; 19 | this.height = h; 20 | this.tiles = new byte[w * h]; 21 | for(int i = 0; i < tiles.length; i++) { 22 | tiles[i] = -128; 23 | } 24 | } 25 | 26 | public void render(Screen screen, Game game, int xOffset, int yOffset, int scale) { 27 | int tSize = Game.SPR_SIZE * scale; 28 | 29 | int xt0 = Math.floorDiv(xOffset, tSize); 30 | int yt0 = Math.floorDiv(yOffset, tSize); 31 | int xt1 = xt0 + (Console.WIDTH / tSize) + 1; 32 | int yt1 = yt0 + (Console.HEIGHT / tSize) + 1; 33 | 34 | for(int yt = yt0; yt < yt1; yt++) { 35 | if(yt < 0 || yt >= height) continue; 36 | 37 | for(int xt = xt0; xt < xt1; xt++) { 38 | if(xt < 0 || xt >= width) continue; 39 | 40 | int id = getTile(xt, yt); 41 | screen.draw(game.getSprite(id, 1, 1).getScaled(scale), 42 | xt * Game.SPR_SIZE * scale - xOffset, 43 | yt * Game.SPR_SIZE * scale - yOffset); 44 | } 45 | } 46 | } 47 | 48 | public int getTile(int x, int y) { 49 | return tiles[x + y * width] + 128; 50 | } 51 | 52 | public void setTile(int x, int y, int id) { 53 | tiles[x + y * width] = (byte) (id - 128); 54 | } 55 | 56 | public static Map load(InputStream inputStream) { 57 | try { 58 | DataInputStream in = new DataInputStream(new BufferedInputStream(inputStream)); 59 | 60 | int w = in.readInt(); 61 | int h = in.readInt(); 62 | Map map = new Map(w, h); 63 | 64 | for(int i = 0; i < map.tiles.length; i++) { 65 | int data = in.read(); 66 | if(data == -1) { 67 | Console.die("Error:\n" 68 | + "map file is malformed\n" 69 | + "(not enought tile data)"); 70 | return null; 71 | } 72 | map.tiles[i] = (byte) (data - 128); 73 | } 74 | in.close(); 75 | return map; 76 | } catch(IOException e) { 77 | throw new RuntimeException(e); 78 | } 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/vulc/luag/game/save/SaveSystem.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.game.save; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.BufferedOutputStream; 5 | import java.io.ByteArrayOutputStream; 6 | import java.io.DataInputStream; 7 | import java.io.DataOutputStream; 8 | import java.io.FileInputStream; 9 | import java.io.FileOutputStream; 10 | import java.io.IOException; 11 | import java.io.OutputStream; 12 | 13 | import org.luaj.vm2.LuaError; 14 | import org.luaj.vm2.LuaTable; 15 | import org.luaj.vm2.LuaValue; 16 | 17 | import vulc.luag.Console; 18 | import vulc.luag.gfx.Icons; 19 | import vulc.vdf.ObjectTag; 20 | 21 | public class SaveSystem { 22 | 23 | public static final String SAVE_EXTENSION = "sav"; 24 | public static final int MAX_SIZE = 256 * 1024; // 256 KB 25 | public static final int SAVE_DELAY = 60; // 1 sec 26 | 27 | private final String saveFile; 28 | 29 | private int lastSave = 0; 30 | private boolean hasLoaded = false; 31 | 32 | public SaveSystem(String cartridgeFile) { 33 | this.saveFile = cartridgeFile + "." + SAVE_EXTENSION; 34 | } 35 | 36 | public void tick() { 37 | // render the "save" icon only if the action was performed recently 38 | if(Console.ticks - lastSave > SAVE_DELAY) return; 39 | 40 | Console.SCREEN.drawBool(Icons.SAVE, 0xffffff, 41 | ((SAVE_DELAY - (Console.ticks - 1 - lastSave)) * 0xff / SAVE_DELAY), 42 | 151, 3); 43 | } 44 | 45 | public int serialize(LuaTable saveTable) { 46 | Console.LOGGER.info("Saving game data..."); 47 | 48 | // if the save action was performed recently, don't save again 49 | if(Console.ticks - lastSave < SAVE_DELAY) { 50 | Console.LOGGER.severe("Save Error 1: trying to save too fast " 51 | + "(delay: " + SAVE_DELAY + " ticks)"); 52 | return 1; // too fast 53 | } 54 | 55 | lastSave = Console.ticks; 56 | 57 | ObjectTag obj = new ObjectTag(); 58 | addTableToObjectTag(obj, saveTable); 59 | 60 | ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 61 | 62 | try(DataOutputStream bufferOut = new DataOutputStream(buffer)) { 63 | obj.serialize(bufferOut); 64 | } catch(IOException e) { 65 | e.printStackTrace(); 66 | } 67 | 68 | if(buffer.size() > MAX_SIZE) { 69 | Console.LOGGER.severe("Save Error 2: too much data " 70 | + "(max: " + MAX_SIZE + " bytes, sent: " + buffer.size() + ")"); 71 | return 2; // too much data 72 | } 73 | 74 | try(OutputStream out = new BufferedOutputStream(new FileOutputStream(saveFile))) { 75 | synchronized(Console.DONT_STOP_LOCK) { 76 | out.write(buffer.toByteArray()); 77 | } 78 | } catch(IOException e) { 79 | e.printStackTrace(); 80 | } 81 | 82 | Console.LOGGER.info("Saving complete: " + buffer.size() + " bytes written"); 83 | return 0; // ok 84 | } 85 | 86 | private static void addTableToObjectTag(ObjectTag obj, LuaTable table) { 87 | for(LuaValue keyLua : table.keys()) { 88 | LuaValue value = table.get(keyLua); 89 | String key = keyLua.toString(); 90 | 91 | // add all valid values to the ObjectTag 92 | if(value.isboolean()) obj.setBoolean(key, value.checkboolean()); 93 | else if(value.isint()) { 94 | int num = value.checkint(); 95 | 96 | if((byte) num == num) obj.setByte(key, (byte) num); 97 | else if((short) num == num) obj.setShort(key, (short) num); 98 | else obj.setInt(key, num); 99 | // long is considered Double by luaj 100 | } else if(value.isnumber()) obj.setDouble(key, value.checkdouble()); 101 | else if(value.isstring()) obj.setString(key, value.tojstring()); 102 | else if(value.istable()) { 103 | ObjectTag subObj = new ObjectTag(); 104 | addTableToObjectTag(subObj, value.checktable()); 105 | obj.setObject(key, subObj); 106 | } else throw new LuaError("bad argument: type is " + value.typename()); 107 | } 108 | } 109 | 110 | public LuaValue deserialize() { 111 | Console.LOGGER.info("Loading game data..."); 112 | 113 | if(hasLoaded) { 114 | Console.LOGGER.severe("Load Error: data can be loaded only once"); 115 | return LuaValue.NIL; 116 | } 117 | hasLoaded = true; 118 | 119 | ObjectTag obj = new ObjectTag(); 120 | try(DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(saveFile)))) { 121 | obj.deserialize(in); 122 | } catch(IOException e) { 123 | e.printStackTrace(); 124 | } 125 | 126 | LuaTable saveTable = new LuaTable(); 127 | addObjectTagToTable(saveTable, obj); 128 | 129 | Console.LOGGER.info("Loading complete"); 130 | return saveTable; 131 | } 132 | 133 | private static void addObjectTagToTable(LuaTable table, ObjectTag obj) { 134 | for(String key : obj.keySet()) { 135 | Object value = obj.getValue(key); 136 | 137 | LuaValue toAdd = null; 138 | if(value instanceof Boolean) toAdd = LuaValue.valueOf((boolean) value); 139 | else if(value instanceof Byte) toAdd = LuaValue.valueOf((byte) value); 140 | else if(value instanceof Short) toAdd = LuaValue.valueOf((short) value); 141 | else if(value instanceof Integer) toAdd = LuaValue.valueOf((int) value); 142 | else if(value instanceof Double) toAdd = LuaValue.valueOf((double) value); 143 | else if(value instanceof String) toAdd = LuaValue.valueOf((String) value); 144 | else if(value instanceof ObjectTag) { 145 | toAdd = new LuaTable(); 146 | addObjectTagToTable((LuaTable) toAdd, (ObjectTag) value); 147 | } else { 148 | throw new LuaError("save file is malformed (contains invalid data type: " 149 | + value.getClass().getCanonicalName() + ")"); 150 | } 151 | if(toAdd != null) table.set(key, toAdd); 152 | } 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /src/vulc/luag/gfx/Colors.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.gfx; 2 | 3 | public abstract class Colors { 4 | 5 | public static final int BACKGROUND_0 = 0xdd4444; 6 | public static final int BACKGROUND_1 = 0xaa4444; 7 | 8 | public static final int FOREGROUND_0 = 0x000000; 9 | public static final int FOREGROUND_1 = 0xeecccc; 10 | public static final int FOREGROUND_HIGHLIGHT = 0xffff55; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/vulc/luag/gfx/ConsoleFrame.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.gfx; 2 | 3 | import java.awt.Color; 4 | import java.awt.Dimension; 5 | import java.awt.Frame; 6 | import java.awt.Insets; 7 | import java.awt.event.WindowAdapter; 8 | import java.awt.event.WindowEvent; 9 | import java.io.IOException; 10 | 11 | import javax.imageio.ImageIO; 12 | 13 | import vulc.luag.Console; 14 | 15 | // BUG try to do WIN+down when in full screen 16 | public class ConsoleFrame extends Frame { 17 | 18 | private static final long serialVersionUID = 1L; 19 | 20 | private int wOld = -1, hOld = -1; 21 | 22 | public void init() { 23 | setTitle(Console.NAME + " " + Console.VERSION); 24 | setBackground(Color.DARK_GRAY); 25 | 26 | try { 27 | setIconImage(ImageIO.read(Console.class.getResourceAsStream("/res/icon.png"))); 28 | } catch(IOException e) { 29 | e.printStackTrace(); 30 | } 31 | 32 | addWindowListener(new WindowAdapter() { 33 | public void windowClosing(WindowEvent e) { 34 | synchronized(Console.DONT_STOP_LOCK) { 35 | System.exit(0); 36 | } 37 | } 38 | 39 | public void windowIconified(WindowEvent e) { 40 | Console.stop(); 41 | } 42 | 43 | public void windowDeiconified(WindowEvent e) { 44 | Console.start(); 45 | } 46 | }); 47 | } 48 | 49 | public void checkSize() { 50 | Dimension d = getSize(); 51 | Insets insets = getInsets(); 52 | int wFrame = d.width - insets.left - insets.right; 53 | int hFrame = d.height - insets.top - insets.bottom; 54 | 55 | if(wFrame != wOld || hFrame != hOld) { 56 | Console.updateScaledSize(wFrame, hFrame); 57 | 58 | wOld = wFrame; 59 | hOld = hFrame; 60 | } 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/vulc/luag/gfx/Icons.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.gfx; 2 | 3 | import javax.imageio.ImageIO; 4 | 5 | import vulc.bitmap.Bitmap; 6 | import vulc.bitmap.BoolBitmap; 7 | 8 | public abstract class Icons { 9 | 10 | // GENERAL 11 | public static final Bitmap SHELL = loadIcon("/res/icons/shell.png"); 12 | public static final Bitmap SAVE = loadIcon("/res/icons/save.png"); 13 | public static final Bitmap UNDO = loadIcon("/res/icons/undo.png"); 14 | public static final Bitmap REDO = loadIcon("/res/icons/redo.png"); 15 | public static final Bitmap SELECTED = loadIcon("/res/icons/selected.png"); 16 | 17 | // EDITORS 18 | public static final Bitmap SPRITE_EDITOR = loadIcon("/res/icons/editor/sprite_editor.png"); 19 | public static final Bitmap MAP_EDITOR = loadIcon("/res/icons/editor/map_editor.png"); 20 | 21 | // TOOLS 22 | public static final Bitmap PENCIL_TOOL = loadIcon("/res/icons/editor/tool/pencil.png"); 23 | public static final Bitmap BUCKET_TOOL = loadIcon("/res/icons/editor/tool/bucket.png"); 24 | public static final Bitmap PICKUP_TOOL = loadIcon("/res/icons/editor/tool/pickup.png"); 25 | public static final Bitmap SELECT_TOOL = loadIcon("/res/icons/editor/tool/select.png"); 26 | 27 | // SPRITE EDITOR 28 | public static final Bitmap SPRITE_SCOPE_1 = loadIcon("/res/icons/editor/sprite/scope_1.png"); 29 | public static final Bitmap SPRITE_SCOPE_2 = loadIcon("/res/icons/editor/sprite/scope_2.png"); 30 | 31 | private static Bitmap loadIcon(String icon) { 32 | try { 33 | return new BoolBitmap(ImageIO.read(Icons.class.getResourceAsStream(icon)), 0x000000); 34 | } catch(Exception e) { 35 | throw new RuntimeException(e); 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/vulc/luag/gfx/Screen.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.gfx; 2 | 3 | import vulc.bitmap.IntBitmap; 4 | import vulc.bitmap.font.Font; 5 | 6 | public class Screen extends IntBitmap { 7 | 8 | public static final Font FONT; 9 | static { 10 | FONT = new Font(Screen.class.getResourceAsStream("/res/font.fv3")); 11 | 12 | FONT.setLetterSpacing(1); 13 | FONT.setLineSpacing(1); 14 | } 15 | 16 | public Screen(int width, int height) { 17 | super(width, height); 18 | setFont(FONT); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/vulc/luag/gfx/gui/GUIButton.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.gfx.gui; 2 | 3 | public class GUIButton extends GUILabel { 4 | 5 | public Runnable onMouseDownAction = null; 6 | public Runnable onMousePressAction = null; 7 | public Runnable onMouseReleaseAction = null; 8 | 9 | public GUIButton(int x, int y, int w, int h) { 10 | super(x, y, w, h); 11 | } 12 | 13 | public void onMouseDown(int xMouse, int yMouse) { 14 | if(onMouseDownAction != null) onMouseDownAction.run(); 15 | } 16 | 17 | public void onMousePress(int xMouse, int yMouse) { 18 | if(onMousePressAction != null) onMousePressAction.run(); 19 | } 20 | 21 | public void onMouseRelease(int xMouse, int yMouse) { 22 | if(onMouseReleaseAction != null) onMouseReleaseAction.run(); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/vulc/luag/gfx/gui/GUIComponent.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.gfx.gui; 2 | 3 | import vulc.luag.gfx.Screen; 4 | 5 | public class GUIComponent { 6 | 7 | public int x, y; 8 | public int w, h; 9 | 10 | public boolean mouseInside = false; 11 | public boolean focused = false; 12 | 13 | public boolean opaque = false; 14 | public int background = 0xDDDDDD; 15 | 16 | public GUIComponent(int x, int y, int w, int h) { 17 | this.x = x; 18 | this.y = y; 19 | this.w = w; 20 | this.h = h; 21 | } 22 | 23 | public void tick() { 24 | } 25 | 26 | public void render(Screen screen) { 27 | if(opaque) screen.fill(x, 28 | y, 29 | x + w - 1, 30 | y + h - 1, 31 | background); 32 | } 33 | 34 | protected boolean isPointInside(int x, int y) { 35 | return x >= 0 && x < w && y >= 0 && y < h; 36 | } 37 | 38 | public void onMouseDown(int xMouse, int yMouse) { 39 | } 40 | 41 | public void onMousePress(int xMouse, int yMouse) { 42 | } 43 | 44 | public void onMouseRelease(int xMouse, int yMouse) { 45 | } 46 | 47 | public void onMouseInside(int xMouse, int yMouse) { 48 | } 49 | 50 | public void onMouseEnter() { 51 | } 52 | 53 | public void onMouseExit() { 54 | } 55 | 56 | public void onGainFocus() { 57 | } 58 | 59 | public void onLostFocus() { 60 | } 61 | 62 | public void onKeyPress(char character) { 63 | } 64 | 65 | public void onMouseScroll(int xMouse, int yMouse, int count) { 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/vulc/luag/gfx/gui/GUILabel.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.gfx.gui; 2 | 3 | import vulc.bitmap.Bitmap; 4 | import vulc.luag.gfx.Screen; 5 | 6 | public class GUILabel extends GUIComponent { 7 | 8 | public String text = ""; 9 | public int textColor = 0; 10 | 11 | public Bitmap boolImage; 12 | public int colorAsBool; 13 | 14 | public Bitmap image; 15 | 16 | public GUILabel(int x, int y, int w, int h) { 17 | super(x, y, w, h); 18 | } 19 | 20 | public void setImage(Bitmap image, int color) { 21 | this.boolImage = image; 22 | this.colorAsBool = color; 23 | 24 | this.image = null; 25 | } 26 | 27 | public void setImage(Bitmap image) { 28 | this.image = image; 29 | 30 | this.boolImage = null; 31 | } 32 | 33 | public void render(Screen screen) { 34 | super.render(screen); 35 | screen.write(text, textColor, x + 1, y + (h - Screen.FONT.getHeight()) / 2); 36 | 37 | if(boolImage != null) { 38 | screen.drawBool(boolImage, colorAsBool, 39 | x + (w - boolImage.width) / 2, 40 | y + (h - boolImage.height) / 2); 41 | } else if(image != null) { 42 | screen.draw(image, 43 | x + (w - image.width) / 2, 44 | y + (h - image.height) / 2); 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/vulc/luag/gfx/gui/GUIMainPanel.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.gfx.gui; 2 | 3 | import java.awt.event.KeyAdapter; 4 | import java.awt.event.KeyEvent; 5 | import java.awt.event.MouseEvent; 6 | import java.awt.event.MouseWheelEvent; 7 | import java.awt.event.MouseWheelListener; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | import vulc.luag.Console; 12 | import vulc.luag.input.InputHandler; 13 | import vulc.luag.input.InputHandler.Key; 14 | import vulc.luag.input.InputHandler.KeyType; 15 | 16 | public class GUIMainPanel extends GUIPanel { 17 | 18 | public final InputHandler input = new InputHandler(); 19 | 20 | private final List keyBuffer = new ArrayList(); 21 | private final KeyAdapter keyListener = new KeyAdapter() { 22 | public void keyPressed(KeyEvent e) { 23 | keyBuffer.add(e.getKeyChar()); 24 | } 25 | }; 26 | 27 | private final Key mouse1 = input.new Key(KeyType.MOUSE, MouseEvent.BUTTON1); 28 | 29 | private int wheelRotCount = 0; 30 | private final MouseWheelListener wheelListener = new MouseWheelListener() { 31 | public void mouseWheelMoved(MouseWheelEvent e) { 32 | wheelRotCount += e.getWheelRotation(); 33 | } 34 | }; 35 | 36 | public GUIMainPanel(int x, int y, int w, int h) { 37 | super(x, y, w, h); 38 | } 39 | 40 | public void init() { 41 | Console console = Console.instance; 42 | 43 | input.init(); 44 | console.addKeyListener(keyListener); 45 | console.addMouseWheelListener(wheelListener); 46 | } 47 | 48 | public void tick() { 49 | super.tick(); 50 | 51 | input.tick(); 52 | 53 | int xMouse = (input.xMouse - Console.xOffset) * Console.WIDTH / Console.scaledWidth - this.x; 54 | int yMouse = (input.yMouse - Console.yOffset) * Console.HEIGHT / Console.scaledHeight - this.y; 55 | 56 | while(keyBuffer.size() != 0) { 57 | char c = keyBuffer.remove(0); 58 | this.onKeyPress(c); 59 | } 60 | 61 | if(mouse1.isKeyDown()) { 62 | if(this.isPointInside(xMouse, yMouse)) { 63 | this.onMouseDown(xMouse, yMouse); 64 | } 65 | } 66 | 67 | if(mouse1.isPressed()) { 68 | if(this.isPointInside(xMouse, yMouse)) { 69 | this.onMousePress(xMouse, yMouse); 70 | } 71 | } 72 | if(mouse1.isReleased()) { 73 | if(this.isPointInside(xMouse, yMouse)) { 74 | this.onMouseRelease(xMouse, yMouse); 75 | } 76 | } 77 | 78 | // do this even if isPointInside is false 79 | this.onMouseInside(xMouse, yMouse); 80 | 81 | if(wheelRotCount != 0) { 82 | if(this.isPointInside(xMouse, yMouse)) { 83 | this.onMouseScroll(xMouse, yMouse, wheelRotCount); 84 | } 85 | wheelRotCount = 0; 86 | } 87 | } 88 | 89 | public void removeInputListeners() { 90 | Console console = Console.instance; 91 | 92 | input.remove(); 93 | console.removeKeyListener(keyListener); 94 | console.removeMouseWheelListener(wheelListener); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/vulc/luag/gfx/gui/GUIPanel.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.gfx.gui; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import vulc.luag.gfx.Screen; 7 | 8 | public class GUIPanel extends GUIComponent { 9 | 10 | protected final List comps = new ArrayList(); 11 | public final Screen screen; 12 | 13 | public GUIPanel(int x, int y, int w, int h) { 14 | super(x, y, w, h); 15 | this.screen = new Screen(w, h); 16 | } 17 | 18 | public void tick() { 19 | for(int i = 0; i < comps.size(); i++) { 20 | GUIComponent comp = comps.get(i); 21 | comp.tick(); 22 | } 23 | } 24 | 25 | public void render(Screen screen) { 26 | this.screen.clear(background); 27 | drawComponents(); 28 | screen.draw(this.screen, x, y); 29 | } 30 | 31 | protected void drawComponents() { 32 | for(int i = 0; i < comps.size(); i++) { 33 | GUIComponent comp = comps.get(i); 34 | comp.render(this.screen); 35 | } 36 | } 37 | 38 | public void add(GUIComponent comp) { 39 | comps.add(comp); 40 | } 41 | 42 | public void remove(GUIComponent comp) { 43 | comps.remove(comp); 44 | } 45 | 46 | public void onMouseDown(int xMouse, int yMouse) { 47 | for(int i = 0; i < comps.size(); i++) { 48 | GUIComponent comp = comps.get(i); 49 | 50 | // relative coordinates 51 | int xr = xMouse - comp.x; 52 | int yr = yMouse - comp.y; 53 | 54 | if(comp.isPointInside(xr, yr)) { 55 | if(!comp.focused) { 56 | comp.focused = true; 57 | comp.onGainFocus(); 58 | } 59 | comp.onMouseDown(xr, yr); 60 | } else { 61 | if(comp.focused) { 62 | comp.focused = false; 63 | comp.onLostFocus(); 64 | } 65 | } 66 | } 67 | } 68 | 69 | public void onMousePress(int xMouse, int yMouse) { 70 | for(int i = 0; i < comps.size(); i++) { 71 | GUIComponent comp = comps.get(i); 72 | 73 | // relative coordinates 74 | int xr = xMouse - comp.x; 75 | int yr = yMouse - comp.y; 76 | 77 | if(comp.isPointInside(xr, yr)) { 78 | comp.onMousePress(xr, yr); 79 | } 80 | } 81 | } 82 | 83 | public void onMouseRelease(int xMouse, int yMouse) { 84 | for(int i = 0; i < comps.size(); i++) { 85 | GUIComponent comp = comps.get(i); 86 | 87 | // relative coordinates 88 | int xr = xMouse - comp.x; 89 | int yr = yMouse - comp.y; 90 | 91 | if(comp.isPointInside(xr, yr)) { 92 | comp.onMouseRelease(xr, yr); 93 | } 94 | } 95 | } 96 | 97 | public void onMouseInside(int xMouse, int yMouse) { 98 | for(int i = 0; i < comps.size(); i++) { 99 | GUIComponent comp = comps.get(i); 100 | 101 | // relative coordinates 102 | int xr = xMouse - comp.x; 103 | int yr = yMouse - comp.y; 104 | 105 | if(comp.isPointInside(xr, yr)) { 106 | if(!comp.mouseInside) { 107 | comp.mouseInside = true; 108 | comp.onMouseEnter(); 109 | } 110 | comp.onMouseInside(xr, yr); 111 | } else { 112 | if(comp.mouseInside) { 113 | comp.mouseInside = false; 114 | comp.onMouseExit(); 115 | } 116 | } 117 | } 118 | } 119 | 120 | public void onMouseExit() { 121 | for(int i = 0; i < comps.size(); i++) { 122 | GUIComponent comp = comps.get(i); 123 | 124 | if(comp.mouseInside) { 125 | comp.mouseInside = false; 126 | comp.onMouseExit(); 127 | } 128 | } 129 | } 130 | 131 | public void onMouseScroll(int xMouse, int yMouse, int count) { 132 | for(int i = 0; i < comps.size(); i++) { 133 | GUIComponent comp = comps.get(i); 134 | 135 | // relative coordinates 136 | int xr = xMouse - comp.x; 137 | int yr = yMouse - comp.y; 138 | 139 | if(comp.isPointInside(xr, yr)) { 140 | comp.onMouseScroll(xr, yr, count); 141 | } 142 | } 143 | } 144 | 145 | public void onKeyPress(char character) { 146 | for(int i = 0; i < comps.size(); i++) { 147 | GUIComponent comp = comps.get(i); 148 | comp.onKeyPress(character); 149 | } 150 | } 151 | 152 | public void onLostFocus() { 153 | for(int i = 0; i < comps.size(); i++) { 154 | GUIComponent comp = comps.get(i); 155 | 156 | if(comp.focused) { 157 | comp.focused = false; 158 | comp.onLostFocus(); 159 | } 160 | } 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /src/vulc/luag/gfx/gui/GUITextBox.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.gfx.gui; 2 | 3 | public class GUITextBox extends GUILabel { 4 | 5 | public static final int ALL_TEXT = 0; 6 | public static final int DEC_ONLY = 1; 7 | public static final int HEX_ONLY = 2; 8 | 9 | public int acceptedText = ALL_TEXT; 10 | public int nChars = -1; 11 | 12 | public GUITextBox(int x, int y, int w, int h) { 13 | super(x, y, w, h); 14 | } 15 | 16 | public void onKeyPress(char character) { 17 | if(!focused) return; 18 | if(character == '\b') { 19 | if(text.length() > 0) text = text.substring(0, text.length() - 1); 20 | } else if(character == '\n') { 21 | onEnterPress(); 22 | } else { 23 | if(text.length() == nChars) return; 24 | boolean canWrite = false; 25 | switch(acceptedText) { 26 | case ALL_TEXT: 27 | if(character >= ' ' && character <= '~') 28 | canWrite = true; 29 | break; 30 | 31 | case DEC_ONLY: 32 | if(character >= '0' && character <= '9') 33 | canWrite = true; 34 | break; 35 | case HEX_ONLY: 36 | if((character >= '0' && character <= '9') 37 | || (character >= 'A' && character <= 'F') 38 | || (character >= 'a' && character <= 'f')) { 39 | canWrite = true; 40 | } 41 | break; 42 | } 43 | if(canWrite) text += character; 44 | } 45 | } 46 | 47 | public void onEnterPress() { 48 | focused = false; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/vulc/luag/gfx/panel/BootPanel.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.gfx.panel; 2 | 3 | import vulc.luag.Console; 4 | import vulc.luag.gfx.Screen; 5 | 6 | public class BootPanel extends Panel { 7 | 8 | public Panel nextPanel; 9 | private int bootTime = 135; // 2.25s 10 | private boolean hasInit = false; 11 | private int animationTicks = 0; 12 | 13 | public BootPanel() { 14 | Console.SCREEN.clear(0); 15 | } 16 | 17 | public void init() { 18 | new Thread() { 19 | public void run() { 20 | nextPanel.init(); 21 | hasInit = true; 22 | } 23 | }.start(); 24 | } 25 | 26 | public void tick() { 27 | Screen screen = Console.SCREEN; 28 | 29 | screen.write(Console.NAME + "\n" 30 | + Console.COPYRIGHT + "\n" 31 | + "Version: " + Console.VERSION, 32 | 0xffffff, 1, 1); 33 | 34 | bootTime--; 35 | if(bootTime <= 0) { 36 | if(hasInit) { 37 | Console.currentPanel = nextPanel; // cannot call console.switchToPanel because it'd call panel.init() again 38 | nextPanel.onShow(); 39 | screen.clear(0); 40 | } else { 41 | int animatPhase = animationTicks / 30 % 4; 42 | String text = null; 43 | if(animatPhase == 0) text = "Loading"; 44 | else if(animatPhase == 1) text = "Loading."; 45 | else if(animatPhase == 2) text = "Loading.."; 46 | else if(animatPhase == 3) text = "Loading..."; 47 | 48 | Console.SCREEN.clear(0); 49 | Console.SCREEN.write(text, 0xffffff, 1, 1); 50 | 51 | animationTicks++; 52 | } 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/vulc/luag/gfx/panel/DeathPanel.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.gfx.panel; 2 | 3 | import vulc.luag.Console; 4 | import vulc.luag.gfx.Screen; 5 | 6 | public class DeathPanel extends Panel { 7 | 8 | private final int background = 0x0000cc; 9 | private final int foreground = 0xffffff; 10 | 11 | private final String text; 12 | private boolean hasTicked = false; 13 | 14 | public DeathPanel(String text) { 15 | this.text = text; 16 | } 17 | 18 | public void tick() { 19 | if(hasTicked) return; 20 | hasTicked = true; 21 | 22 | Screen screen = Console.SCREEN; 23 | screen.clear(background); 24 | 25 | screen.write(text, foreground, 1, 1); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/vulc/luag/gfx/panel/EditorPanel.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.gfx.panel; 2 | 3 | import vulc.luag.Console; 4 | import vulc.luag.editor.Editor; 5 | import vulc.luag.editor.map.MapEditor; 6 | import vulc.luag.editor.sprite.SpriteEditor; 7 | import vulc.luag.game.Game; 8 | import vulc.luag.gfx.Colors; 9 | import vulc.luag.gfx.Icons; 10 | import vulc.luag.gfx.gui.GUIButton; 11 | import vulc.luag.gfx.gui.GUILabel; 12 | import vulc.luag.gfx.gui.GUIMainPanel; 13 | import vulc.luag.gfx.gui.GUIPanel; 14 | 15 | public class EditorPanel extends Panel { 16 | 17 | private final int btnDist = 3; 18 | 19 | private final GUIButton saveBtn; 20 | private final GUILabel footerLabel; 21 | 22 | public final Game game; 23 | public final GUIMainPanel mainPanel; 24 | public Editor currentEditor; 25 | 26 | public Editor mapEditor; 27 | public Editor spriteEditor; 28 | 29 | public EditorPanel() { 30 | this.game = new Game(); 31 | mainPanel = new GUIMainPanel(0, 0, Console.SCREEN.width, Console.SCREEN.height); 32 | mainPanel.background = 0x000000; 33 | mainPanel.init(); 34 | 35 | // 36 | //---HEADER---\\ 37 | // 38 | 39 | GUIPanel headerPanel = new GUIPanel(0, 0, mainPanel.w, 10); 40 | headerPanel.background = Colors.BACKGROUND_0; 41 | mainPanel.add(headerPanel); 42 | 43 | GUIButton shellBtn = new GUIButton(1, 1, 8, 8); 44 | shellBtn.opaque = true; 45 | shellBtn.background = Colors.BACKGROUND_1; 46 | shellBtn.setImage(Icons.SHELL, Colors.FOREGROUND_1); 47 | shellBtn.onMousePressAction = () -> { 48 | Console.switchToPanel(new ShellPanel()); 49 | }; 50 | headerPanel.add(shellBtn); 51 | 52 | GUIButton mapEditBtn = new GUIButton(shellBtn.x + shellBtn.w + btnDist, 1, 8, 8); 53 | mapEditBtn.opaque = true; 54 | mapEditBtn.background = Colors.BACKGROUND_1; 55 | mapEditBtn.setImage(Icons.MAP_EDITOR, Colors.FOREGROUND_1); 56 | mapEditBtn.onMousePressAction = () -> { 57 | switchToEditor(mapEditor); 58 | }; 59 | headerPanel.add(mapEditBtn); 60 | 61 | GUIButton sprEditBtn = new GUIButton(mapEditBtn.x + mapEditBtn.w + btnDist, 1, 8, 8); 62 | sprEditBtn.opaque = true; 63 | sprEditBtn.background = Colors.BACKGROUND_1; 64 | sprEditBtn.setImage(Icons.SPRITE_EDITOR, Colors.FOREGROUND_1); 65 | sprEditBtn.onMousePressAction = () -> { 66 | switchToEditor(spriteEditor); 67 | }; 68 | headerPanel.add(sprEditBtn); 69 | 70 | saveBtn = new GUIButton(headerPanel.w - 9, 1, 8, 8); 71 | saveBtn.opaque = true; 72 | saveBtn.background = Colors.BACKGROUND_1; 73 | saveBtn.setImage(Icons.SAVE, Colors.FOREGROUND_1); 74 | saveBtn.onMousePressAction = () -> { 75 | Console.LOGGER.info("saving"); 76 | 77 | mapEditor.onSave(); 78 | spriteEditor.onSave(); 79 | }; 80 | headerPanel.add(saveBtn); 81 | 82 | // 83 | //---FOOTER---\\ 84 | // 85 | 86 | GUIPanel footerPanel = new GUIPanel(0, mainPanel.h - 10, mainPanel.w, 10); 87 | footerPanel.background = Colors.BACKGROUND_0; 88 | mainPanel.add(footerPanel); 89 | 90 | footerLabel = new GUILabel(1, 1, footerPanel.w - 2, 8); 91 | footerLabel.textColor = Colors.FOREGROUND_1; 92 | footerPanel.add(footerLabel); 93 | } 94 | 95 | public void init() { 96 | if(!game.initDevResources()) return; 97 | 98 | mapEditor = new MapEditor(this, 0, 10, mainPanel.w, mainPanel.h - 20); 99 | spriteEditor = new SpriteEditor(this, 0, 10, mainPanel.w, mainPanel.h - 20); 100 | 101 | switchToEditor(mapEditor); 102 | } 103 | 104 | public void remove() { 105 | game.remove(); 106 | 107 | if(mapEditor != null) mapEditor.remove(); 108 | if(spriteEditor != null) spriteEditor.remove(); 109 | 110 | mainPanel.removeInputListeners(); 111 | } 112 | 113 | public void tick() { 114 | currentEditor.tick(); 115 | mainPanel.tick(); 116 | mainPanel.render(Console.SCREEN); 117 | 118 | if(mapEditor.shouldSave() 119 | || spriteEditor.shouldSave()) { 120 | saveBtn.colorAsBool = Colors.FOREGROUND_HIGHLIGHT; 121 | } else { 122 | saveBtn.colorAsBool = Colors.FOREGROUND_1; 123 | } 124 | } 125 | 126 | private void switchToEditor(Editor editor) { 127 | if(currentEditor != null) currentEditor.remove(); 128 | editor.onShow(); 129 | currentEditor = editor; 130 | 131 | footerLabel.text = editor.getTitle(); 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/vulc/luag/gfx/panel/GamePanel.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.gfx.panel; 2 | 3 | import vulc.luag.Console; 4 | import vulc.luag.game.Game; 5 | 6 | public class GamePanel extends Panel { 7 | 8 | private final Game game; 9 | 10 | public GamePanel() { 11 | this.game = new Game(); 12 | } 13 | 14 | public void init() { 15 | try { 16 | if(Console.cartridge != null) { 17 | if(!game.initCartridgeResources()) return; 18 | } else { 19 | if(!game.initDevResources()) return; 20 | } 21 | game.initScript(); 22 | } catch(Exception e) { 23 | // no error should be uncaught 24 | // this prevents the console to crash 25 | Console.die("Error:\n" 26 | + "uncaught error"); 27 | e.printStackTrace(); 28 | } 29 | } 30 | 31 | public void remove() { 32 | game.remove(); 33 | 34 | // Game's interface can set screen's transparent colors. 35 | // This will reset it. 36 | Console.SCREEN.setTransparent(); 37 | } 38 | 39 | public void tick() { 40 | game.tick(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/vulc/luag/gfx/panel/Panel.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.gfx.panel; 2 | 3 | public abstract class Panel { 4 | 5 | public void init() { 6 | } 7 | 8 | public void onShow() { 9 | } 10 | 11 | public void remove() { 12 | } 13 | 14 | public abstract void tick(); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/vulc/luag/gfx/panel/ShellPanel.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.gfx.panel; 2 | 3 | import java.awt.event.KeyAdapter; 4 | import java.awt.event.KeyEvent; 5 | import java.awt.event.MouseWheelEvent; 6 | import java.awt.event.MouseWheelListener; 7 | 8 | import vulc.luag.Console; 9 | import vulc.luag.input.InputHandler; 10 | import vulc.luag.input.InputHandler.Key; 11 | import vulc.luag.input.InputHandler.KeyType; 12 | import vulc.luag.shell.Shell; 13 | import vulc.luag.shell.ShellChar; 14 | 15 | public class ShellPanel extends Panel { 16 | 17 | private final InputHandler input = new InputHandler(); 18 | public final Key ctrl = input.new Key(KeyType.KEYBOARD, KeyEvent.VK_CONTROL); 19 | 20 | private final KeyAdapter keyListener = new KeyAdapter() { 21 | public void keyPressed(KeyEvent e) { 22 | Shell.USER_BUFFER.add(new ShellChar(e.getKeyChar(), Shell.USER_FOREGROUND)); 23 | 24 | int code = e.getKeyCode(); 25 | if(code == KeyEvent.VK_UP) 26 | Shell.pressedUP = true; 27 | else if(code == KeyEvent.VK_LEFT) 28 | Shell.pressedLEFT = true; 29 | else if(code == KeyEvent.VK_DOWN) 30 | Shell.pressedDOWN = true; 31 | else if(code == KeyEvent.VK_RIGHT) 32 | Shell.pressedRIGHT = true; 33 | } 34 | }; 35 | private final MouseWheelListener mouseScrollListener = new MouseWheelListener() { 36 | public void mouseWheelMoved(MouseWheelEvent e) { 37 | Shell.scrollBuffer += e.getWheelRotation(); 38 | } 39 | }; 40 | 41 | public ShellPanel() { 42 | Shell.panel = this; 43 | } 44 | 45 | public void onShow() { 46 | Console console = Console.instance; 47 | 48 | input.init(); 49 | console.addKeyListener(keyListener); 50 | console.addMouseWheelListener(mouseScrollListener); 51 | } 52 | 53 | public void tick() { 54 | input.tick(); 55 | Shell.tick(); 56 | } 57 | 58 | public void remove() { 59 | Console console = Console.instance; 60 | 61 | input.remove(); 62 | console.removeKeyListener(keyListener); 63 | console.removeMouseWheelListener(mouseScrollListener); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/vulc/luag/input/FullScreenListener.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.input; 2 | 3 | import java.awt.event.KeyEvent; 4 | import java.awt.event.KeyListener; 5 | 6 | import vulc.luag.Console; 7 | 8 | public class FullScreenListener implements KeyListener { 9 | 10 | public void keyTyped(KeyEvent e) { 11 | } 12 | 13 | public void keyPressed(KeyEvent e) { 14 | if(e.getKeyCode() == KeyEvent.VK_F11) { 15 | Console.switchFullScreen(); 16 | } 17 | } 18 | 19 | public void keyReleased(KeyEvent e) { 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/vulc/luag/input/InputHandler.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (C) 2019 Vulcalien 3 | * This code or part of it is licensed under MIT License by Vulcalien 4 | ******************************************************************************/ 5 | package vulc.luag.input; 6 | 7 | import java.awt.event.FocusEvent; 8 | import java.awt.event.FocusListener; 9 | import java.awt.event.KeyEvent; 10 | import java.awt.event.KeyListener; 11 | import java.awt.event.MouseEvent; 12 | import java.awt.event.MouseListener; 13 | import java.awt.event.MouseMotionListener; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | import vulc.luag.Console; 18 | 19 | public class InputHandler implements KeyListener, MouseListener, MouseMotionListener, FocusListener { 20 | 21 | public static enum KeyType { 22 | KEYBOARD, MOUSE 23 | } 24 | 25 | public static enum KeyAction { 26 | PRESS, RELEASE 27 | } 28 | 29 | private final List keys = new ArrayList(); 30 | private final List keyboardKeys = new ArrayList(); 31 | private final List mouseKeys = new ArrayList(); 32 | 33 | public int xMouse = -1, yMouse = -1; 34 | 35 | public void init() { 36 | Console console = Console.instance; 37 | 38 | console.setFocusTraversalKeysEnabled(false); 39 | 40 | console.addKeyListener(this); 41 | console.addMouseListener(this); 42 | console.addMouseMotionListener(this); 43 | console.addFocusListener(this); 44 | } 45 | 46 | public void remove() { 47 | Console console = Console.instance; 48 | 49 | releaseAll(); 50 | 51 | console.removeKeyListener(this); 52 | console.removeMouseListener(this); 53 | console.removeMouseMotionListener(this); 54 | console.removeFocusListener(this); 55 | } 56 | 57 | public void tick() { 58 | for(int i = 0; i < keys.size(); i++) { 59 | keys.get(i).tick(); 60 | } 61 | } 62 | 63 | private void releaseAll() { 64 | for(Key key : keys) { 65 | key.isKeyDown = false; 66 | key.wasKeyDown = false; 67 | } 68 | } 69 | 70 | private void receiveInput(KeyAction action, KeyType type, int code) { 71 | List keys = getList(type); 72 | for(Key key : keys) { 73 | if(key.code == code) { 74 | if(action == KeyAction.PRESS) { 75 | key.shouldStayDown = true; 76 | } else if(action == KeyAction.RELEASE) { 77 | key.shouldRelease = true; 78 | } 79 | } 80 | } 81 | } 82 | 83 | private List getList(KeyType type) { 84 | switch(type) { 85 | case KEYBOARD: 86 | return keyboardKeys; 87 | 88 | case MOUSE: 89 | return mouseKeys; 90 | 91 | default: 92 | return null; 93 | } 94 | } 95 | 96 | public void keyTyped(KeyEvent e) { 97 | } 98 | 99 | public void keyPressed(KeyEvent e) { 100 | receiveInput(KeyAction.PRESS, KeyType.KEYBOARD, e.getKeyCode()); 101 | } 102 | 103 | public void keyReleased(KeyEvent e) { 104 | receiveInput(KeyAction.RELEASE, KeyType.KEYBOARD, e.getKeyCode()); 105 | } 106 | 107 | public void mouseClicked(MouseEvent e) { 108 | } 109 | 110 | public void mousePressed(MouseEvent e) { 111 | receiveInput(KeyAction.PRESS, KeyType.MOUSE, e.getButton()); 112 | } 113 | 114 | public void mouseReleased(MouseEvent e) { 115 | receiveInput(KeyAction.RELEASE, KeyType.MOUSE, e.getButton()); 116 | } 117 | 118 | public void mouseEntered(MouseEvent e) { 119 | } 120 | 121 | public void mouseExited(MouseEvent e) { 122 | } 123 | 124 | public void mouseDragged(MouseEvent e) { 125 | xMouse = e.getX(); 126 | yMouse = e.getY(); 127 | } 128 | 129 | public void mouseMoved(MouseEvent e) { 130 | xMouse = e.getX(); 131 | yMouse = e.getY(); 132 | } 133 | 134 | public void focusGained(FocusEvent e) { 135 | } 136 | 137 | public void focusLost(FocusEvent e) { 138 | for(Key key : keys) { 139 | key.shouldRelease = true; 140 | } 141 | } 142 | 143 | public class Key { 144 | 145 | private KeyType type; 146 | private int code; 147 | 148 | private boolean shouldStayDown = false; 149 | private boolean shouldRelease = false; 150 | 151 | private boolean isKeyDown = false; 152 | private boolean wasKeyDown = false; 153 | 154 | public Key() { 155 | } 156 | 157 | public Key(KeyType type, int code) { 158 | init(type, code); 159 | } 160 | 161 | private void init(KeyType type, int code) { 162 | this.type = type; 163 | this.code = code; 164 | 165 | keys.add(this); 166 | getList(type).add(this); 167 | } 168 | 169 | private void tick() { 170 | wasKeyDown = isKeyDown; 171 | isKeyDown = shouldStayDown; 172 | 173 | if(shouldRelease) { 174 | shouldRelease = false; 175 | shouldStayDown = false; 176 | } 177 | } 178 | 179 | public void setKeyBinding(KeyType newType, int newCode) { 180 | if(this.type != null) getList(this.type).remove(this); 181 | init(newType, newCode); 182 | } 183 | 184 | public boolean isKeyDown() { 185 | return isKeyDown; 186 | } 187 | 188 | public boolean isPressed() { 189 | return !wasKeyDown && isKeyDown; 190 | } 191 | 192 | public boolean isReleased() { 193 | return wasKeyDown && !isKeyDown; 194 | } 195 | 196 | } 197 | 198 | } 199 | -------------------------------------------------------------------------------- /src/vulc/luag/sfx/Sound.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (C) 2019 Vulcalien 3 | * This code or part of it is licensed under MIT License by Vulcalien 4 | ******************************************************************************/ 5 | package vulc.luag.sfx; 6 | 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | 10 | import javax.sound.sampled.AudioInputStream; 11 | import javax.sound.sampled.AudioSystem; 12 | import javax.sound.sampled.Clip; 13 | 14 | public class Sound { 15 | 16 | private final InputStream inputStream; 17 | private Clip clip; 18 | 19 | public Sound(InputStream in) { 20 | this.inputStream = in; 21 | try { 22 | AudioInputStream ais = AudioSystem.getAudioInputStream(in); 23 | Clip clip = AudioSystem.getClip(); 24 | clip.open(ais); 25 | this.clip = clip; 26 | } catch(Exception e) { 27 | e.printStackTrace(); 28 | } 29 | } 30 | 31 | public void onRemove() { 32 | stop(); 33 | try { 34 | inputStream.close(); 35 | } catch(IOException e) { 36 | e.printStackTrace(); 37 | } 38 | } 39 | 40 | public void play() { 41 | if(clip == null) return; 42 | if(clip.isRunning()) clip.stop(); 43 | clip.setFramePosition(0); 44 | clip.start(); 45 | } 46 | 47 | public void loop() { 48 | if(clip == null) return; 49 | if(clip.isRunning()) clip.stop(); 50 | clip.setFramePosition(0); 51 | clip.loop(Clip.LOOP_CONTINUOUSLY); 52 | } 53 | 54 | public void stop() { 55 | if(clip == null) return; 56 | clip.stop(); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/vulc/luag/shell/Shell.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.shell; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import vulc.bitmap.font.Font; 7 | import vulc.luag.Console; 8 | import vulc.luag.gfx.Screen; 9 | import vulc.luag.gfx.panel.ShellPanel; 10 | import vulc.luag.shell.command.ShellCommand; 11 | 12 | public abstract class Shell { 13 | 14 | public static final int BACKGROUND = 0x000000; 15 | public static final int DEFAULT_FOREGROUND = 0xffffff; 16 | public static final int USER_FOREGROUND = 0x00ff00; 17 | public static final int ERROR_FOREGROUND = 0xff0000; 18 | 19 | public static final int HORIZONTAL_CHARS = Console.WIDTH / (Screen.FONT.widthOf(' ') + 1); 20 | public static final int VERTICAL_LINES = Console.HEIGHT / (Screen.FONT.getHeight() + 1); 21 | 22 | private static final int ANIMATION_DELAY = 25; 23 | 24 | public static final List CONSOLE_BUFFER = new ArrayList(); // chars written by the console 25 | public static final List USER_BUFFER = new ArrayList(); // chars written by the user 26 | 27 | public static int scrollBuffer = 0; 28 | public static boolean pressedUP = false, pressedLEFT = false, pressedDOWN = false, pressedRIGHT = false; 29 | 30 | private static final List CLOSED_ROWS = new ArrayList(); 31 | private static ShellRow currentLine = new ShellRow(); 32 | private static int currentChar = 0; 33 | 34 | private static final List COMMAND_HISTORY = new ArrayList(); 35 | private static int historyPoint = 0; 36 | 37 | private static int renderOffset = 0; 38 | private static int animationTicks = 0; // cursor animation 39 | 40 | public static ShellPanel panel; 41 | 42 | private Shell() { 43 | } 44 | 45 | public static void init() { 46 | write(Console.NAME + "\n"); 47 | write(Console.COPYRIGHT + "\n"); 48 | write("Version: " + Console.VERSION + "\n"); 49 | write("\n"); 50 | } 51 | 52 | public static void tick() { 53 | if(CONSOLE_BUFFER.size() != 0) { 54 | ShellChar c = CONSOLE_BUFFER.remove(0); 55 | receiveInput(c, false); 56 | 57 | animationTicks = ANIMATION_DELAY; 58 | } else if(USER_BUFFER.size() != 0) { 59 | ShellChar c = USER_BUFFER.remove(0); 60 | receiveInput(c, true); 61 | 62 | animationTicks = 0; 63 | } else { 64 | animationTicks++; 65 | } 66 | 67 | // history 68 | byte historyShift = 0; 69 | if(pressedUP) { 70 | pressedUP = false; 71 | historyShift++; 72 | } 73 | if(pressedDOWN) { 74 | pressedDOWN = false; 75 | historyShift--; 76 | } 77 | 78 | if(historyShift != 0) { 79 | int newHistoryPoint = historyPoint + historyShift; 80 | 81 | if(newHistoryPoint >= 0 && newHistoryPoint < COMMAND_HISTORY.size()) { 82 | historyPoint = newHistoryPoint; 83 | 84 | // most recent command is 0 85 | currentLine = COMMAND_HISTORY.get(historyPoint).copy(); 86 | currentChar = currentLine.size(); 87 | } 88 | } 89 | 90 | if(scrollBuffer != 0) { 91 | int newOffset = renderOffset + scrollBuffer; 92 | 93 | // if is under the bottom then move back to bottom 94 | int lowestOffset = lowestOffset(); 95 | if(newOffset > lowestOffset) { 96 | newOffset = lowestOffset; 97 | } 98 | 99 | // this may be caused by the previous if block 100 | if(newOffset < 0) newOffset = 0; 101 | 102 | renderOffset = newOffset; 103 | scrollBuffer = 0; 104 | } 105 | 106 | if(pressedLEFT) { 107 | pressedLEFT = false; 108 | if(currentChar > 0) { 109 | if(panel.ctrl.isKeyDown()) { 110 | currentChar = leftWordPosition(); 111 | } else { 112 | currentChar--; 113 | } 114 | } 115 | } 116 | if(pressedRIGHT) { 117 | pressedRIGHT = false; 118 | if(currentChar < currentLine.size()) { 119 | if(panel.ctrl.isKeyDown()) { 120 | currentChar = rightWordPosition(); 121 | } else { 122 | currentChar++; 123 | } 124 | } 125 | } 126 | render(); 127 | } 128 | 129 | private static void render() { 130 | Console.SCREEN.clear(BACKGROUND); 131 | 132 | int margin = 1; 133 | int yRender = margin; 134 | 135 | for(int i = 0; i < VERTICAL_LINES; i++) { 136 | int line = i + renderOffset; 137 | 138 | if(line < CLOSED_ROWS.size()) { 139 | CLOSED_ROWS.get(line).render(Console.SCREEN, margin, yRender); 140 | yRender += Screen.FONT.getHeight() + Screen.FONT.getLineSpacing(); 141 | } else { 142 | ShellRow[] splitCurrent = splitCurrentLine(currentLine); 143 | for(int a = 0; a < splitCurrent.length; a++) { 144 | splitCurrent[a].render(Console.SCREEN, margin, yRender); 145 | yRender += Screen.FONT.getHeight() + Screen.FONT.getLineSpacing(); 146 | } 147 | break; 148 | } 149 | } 150 | 151 | // draw the cursor 152 | if(animationTicks / ANIMATION_DELAY % 2 == 0) { 153 | Font font = Screen.FONT; 154 | Console.SCREEN.write("_", DEFAULT_FOREGROUND, 155 | 1 + (font.widthOf(' ') + font.getLetterSpacing()) 156 | * (currentChar % HORIZONTAL_CHARS), 157 | 1 + (font.getHeight() + font.getLineSpacing()) 158 | * (currentChar / HORIZONTAL_CHARS + CLOSED_ROWS.size() - renderOffset)); 159 | } 160 | } 161 | 162 | public static void receiveInput(ShellChar character, boolean shouldExecute) { 163 | int lowestOffset; 164 | switch(character.value) { 165 | case '\n': 166 | if(shouldExecute) execute(currentLine); 167 | 168 | ShellRow[] splitCurrent = splitCurrentLine(currentLine); 169 | for(int i = 0; i < splitCurrent.length; i++) { 170 | // here the text always ends with '\n' 171 | CLOSED_ROWS.add(splitCurrent[i]); 172 | } 173 | currentLine.clear(); 174 | currentChar = 0; 175 | 176 | // if is not at bottom then move to bottom 177 | lowestOffset = lowestOffset(); 178 | if(renderOffset < lowestOffset) { 179 | renderOffset = lowestOffset; 180 | } 181 | break; 182 | 183 | case '\b': 184 | if(currentLine.size() > 0 && currentChar > 0) { 185 | if(panel.ctrl.isKeyDown()) { 186 | int oldLength = currentLine.size(); 187 | currentLine = currentLine.subrow(0, leftWordPosition()) 188 | .join(currentLine.subrow(currentChar, currentLine.size())); 189 | 190 | currentChar -= oldLength - currentLine.size(); 191 | } else { 192 | currentLine = currentLine.subrow(0, currentChar - 1) 193 | .join(currentLine.subrow(currentChar, currentLine.size())); 194 | currentChar--; 195 | } 196 | } 197 | break; 198 | 199 | case 127: 200 | if(currentLine.size() > 0 && currentChar != currentLine.size()) { 201 | if(panel.ctrl.isKeyDown()) { 202 | currentLine = currentLine.subrow(0, currentChar) 203 | .join(currentLine.subrow(rightWordPosition(), currentLine.size())); 204 | } else { 205 | currentLine = currentLine.subrow(0, currentChar) 206 | .join(currentLine.subrow(currentChar + 1, currentLine.size())); 207 | } 208 | } 209 | break; 210 | 211 | default: 212 | if(character.value >= 32 && character.value < 127) { 213 | currentLine = currentLine.subrow(0, currentChar) 214 | .join(character) 215 | .join(currentLine.subrow(currentChar, currentLine.size())); 216 | currentChar++; 217 | 218 | // if is not at bottom then move to bottom 219 | lowestOffset = lowestOffset(); 220 | if(renderOffset < lowestOffset) { 221 | renderOffset = lowestOffset; 222 | } 223 | } 224 | break; 225 | } 226 | } 227 | 228 | public static void write(String text, int color) { 229 | for(int i = 0; i < text.length(); i++) { 230 | CONSOLE_BUFFER.add(new ShellChar(text.charAt(i), color)); 231 | } 232 | } 233 | 234 | public static void write(String text) { 235 | write(text, DEFAULT_FOREGROUND); 236 | } 237 | 238 | public static void clear() { 239 | CLOSED_ROWS.clear(); 240 | currentLine.clear(); 241 | renderOffset = 0; 242 | } 243 | 244 | public static void execute(ShellRow row) { 245 | COMMAND_HISTORY.add(0, row.copy()); 246 | historyPoint = -1; 247 | 248 | String line = row.toString(); 249 | line = line.trim().replaceAll(" +", " "); 250 | if(!ShellCommand.execute(line)) { 251 | write("unknown command\n\n"); 252 | } 253 | } 254 | 255 | private static ShellRow[] splitCurrentLine(ShellRow line) { 256 | int height = (line.size() - 1) / (Shell.HORIZONTAL_CHARS) + 1; 257 | 258 | ShellRow[] splitLine = new ShellRow[height]; 259 | for(int i = 0; i < height; i++) { 260 | int start = i * Shell.HORIZONTAL_CHARS; 261 | 262 | int end = start; 263 | if(i != height - 1) { 264 | end += Shell.HORIZONTAL_CHARS; 265 | } else { 266 | // if length == HORIZONTAL_CHARS the % will be 0 267 | if(line.size() != 0 268 | && line.size() % Shell.HORIZONTAL_CHARS == 0) { 269 | end += Shell.HORIZONTAL_CHARS; 270 | } else { 271 | end += line.size() % (Shell.HORIZONTAL_CHARS); 272 | } 273 | } 274 | splitLine[i] = line.subrow(start, end); 275 | } 276 | return splitLine; 277 | } 278 | 279 | private static int lowestOffset() { 280 | int currentLineHeight = currentLine.size() / HORIZONTAL_CHARS + 1; 281 | return CLOSED_ROWS.size() - VERTICAL_LINES + currentLineHeight; 282 | } 283 | 284 | // find left word's position, used for ctrl+left and ctrl+'\b' 285 | private static int leftWordPosition() { 286 | int spacePosition = -1; 287 | boolean foundNonSpace = false; 288 | for(int i = currentChar - 1; i >= 0; i--) { 289 | if(currentLine.charAt(i) == ' ') { 290 | if(foundNonSpace) { 291 | spacePosition = i; 292 | break; 293 | } 294 | } else { 295 | foundNonSpace = true; 296 | } 297 | } 298 | return spacePosition + 1; 299 | } 300 | 301 | // find right word's position, used for ctrl+right and ctrl+del 302 | private static int rightWordPosition() { 303 | int nonSpacePosition = currentLine.size(); 304 | boolean foundSpace = false; 305 | for(int i = currentChar; i < currentLine.size(); i++) { 306 | if(currentLine.charAt(i) != ' ') { 307 | if(foundSpace) { 308 | nonSpacePosition = i; 309 | break; 310 | } 311 | } else { 312 | foundSpace = true; 313 | } 314 | } 315 | return nonSpacePosition; 316 | } 317 | 318 | } 319 | -------------------------------------------------------------------------------- /src/vulc/luag/shell/ShellChar.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.shell; 2 | 3 | public class ShellChar { 4 | 5 | public final char value; 6 | public final int color; 7 | 8 | public ShellChar(char value, int color) { 9 | this.value = value; 10 | this.color = color; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/vulc/luag/shell/ShellRow.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.shell; 2 | 3 | import java.util.ArrayList; 4 | 5 | import vulc.bitmap.Bitmap; 6 | import vulc.luag.gfx.Screen; 7 | 8 | public class ShellRow extends ArrayList { 9 | 10 | private static final long serialVersionUID = 1L; 11 | 12 | public void render(Bitmap bitmap, int x, int y) { 13 | for(ShellChar c : this) { 14 | bitmap.write(c.value + "", c.color, x, y); 15 | x += Screen.FONT.widthOf(c.value) + Screen.FONT.getLetterSpacing(); 16 | } 17 | } 18 | 19 | public char charAt(int index) { 20 | return get(index).value; 21 | } 22 | 23 | public ShellRow join(ShellChar c) { 24 | add(c); 25 | return this; 26 | } 27 | 28 | public ShellRow join(ShellRow row) { 29 | for(ShellChar c : row) { 30 | add(c); 31 | } 32 | return this; 33 | } 34 | 35 | public ShellRow subrow(int begin, int end) { 36 | ShellRow result = new ShellRow(); 37 | for(int i = begin; i < end; i++) { 38 | result.add(get(i)); 39 | } 40 | return result; 41 | } 42 | 43 | public String toString() { 44 | String string = ""; 45 | for(ShellChar c : this) { 46 | string += c.value; 47 | } 48 | return string; 49 | } 50 | 51 | public ShellRow copy() { 52 | ShellRow newRow = new ShellRow(); 53 | for(ShellChar c : this) { 54 | newRow.add(c); 55 | } 56 | return newRow; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/vulc/luag/shell/command/ClsCommand.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.shell.command; 2 | 3 | import vulc.luag.shell.Shell; 4 | 5 | public class ClsCommand extends ShellCommand { 6 | 7 | public ClsCommand() { 8 | super("cls", "clear"); 9 | } 10 | 11 | public void run(String[] args) { 12 | Shell.clear(); 13 | } 14 | 15 | protected String getHelpMessage() { 16 | return "clears the shell"; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/vulc/luag/shell/command/EditCommand.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.shell.command; 2 | 3 | import vulc.luag.Console; 4 | import vulc.luag.gfx.panel.EditorPanel; 5 | 6 | public class EditCommand extends ShellCommand { 7 | 8 | public EditCommand() { 9 | super("edit", "editor"); 10 | isDevelopersOnly = true; 11 | } 12 | 13 | public void run(String[] args) { 14 | Console.switchToPanel(new EditorPanel()); 15 | } 16 | 17 | protected String getHelpMessage() { 18 | return "opens the game editor"; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/vulc/luag/shell/command/ExitCommand.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.shell.command; 2 | 3 | public class ExitCommand extends ShellCommand { 4 | 5 | public ExitCommand() { 6 | super("exit"); 7 | } 8 | 9 | public void run(String[] args) { 10 | System.exit(0); 11 | } 12 | 13 | protected String getHelpMessage() { 14 | return "shuts down the console"; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/vulc/luag/shell/command/FilesCommand.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.shell.command; 2 | 3 | import java.awt.Desktop; 4 | import java.io.File; 5 | import java.io.IOException; 6 | 7 | import vulc.luag.Console; 8 | import vulc.luag.game.Game; 9 | 10 | public class FilesCommand extends ShellCommand { 11 | 12 | public FilesCommand() { 13 | super("files"); 14 | isDevelopersOnly = true; 15 | } 16 | 17 | public void run(String[] args) { 18 | if(Desktop.isDesktopSupported()) { 19 | Desktop desktop = Desktop.getDesktop(); 20 | 21 | File folder = new File(Game.USERDATA_DIR); 22 | if(!folder.isDirectory()) { 23 | Console.die("Error:\n" 24 | + "'" + Game.USERDATA_DIR_NAME + "'\n" 25 | + "folder not found"); 26 | return; 27 | } 28 | 29 | try { 30 | desktop.open(folder); 31 | } catch(IOException e) { 32 | e.printStackTrace(); 33 | } 34 | } 35 | } 36 | 37 | protected String getHelpMessage() { 38 | return "opens the folder\n" 39 | + "'" + Game.USERDATA_DIR_NAME + "'\n" 40 | + "in the file explorer"; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/vulc/luag/shell/command/HelpCommand.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.shell.command; 2 | 3 | import vulc.luag.shell.Shell; 4 | 5 | public class HelpCommand extends ShellCommand { 6 | 7 | public HelpCommand() { 8 | super("help"); 9 | } 10 | 11 | public void run(String[] args) { 12 | if(args.length > 0) { 13 | String commandName = args[0]; 14 | 15 | ShellCommand command = ShellCommand.findCommand(commandName); 16 | if(command == null) { 17 | Shell.write("help: command not found\n\n"); 18 | } else { 19 | Shell.write(command.getHelpMessage() + "\n\n"); 20 | } 21 | } else { 22 | Shell.write("run: runs game\n"); 23 | Shell.write("edit: opens editor\n"); 24 | Shell.write("pack: creates cartridge\n"); 25 | Shell.write("setup: creates game files\n"); 26 | Shell.write("cls: clears shell\n"); 27 | Shell.write("ver: prints version\n"); 28 | Shell.write("help: prints this list\n"); 29 | Shell.write("mode: changes console mode\n"); 30 | Shell.write("files: opens game folder\n"); 31 | Shell.write("log: opens log file\n"); 32 | Shell.write("\n"); 33 | } 34 | } 35 | 36 | protected String getHelpMessage() { 37 | return "'help'\n" 38 | + "prints a list of commands\n" 39 | + "'help '\n" 40 | + "prints an help message for\n" 41 | + "the given command"; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/vulc/luag/shell/command/LogCommand.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.shell.command; 2 | 3 | import java.awt.Desktop; 4 | import java.io.File; 5 | import java.io.IOException; 6 | 7 | import vulc.luag.Console; 8 | 9 | public class LogCommand extends ShellCommand { 10 | 11 | public LogCommand() { 12 | super("log"); 13 | isDevelopersOnly = true; 14 | } 15 | 16 | protected void run(String[] args) { 17 | // if(args.length < 1) { 18 | // openFile(); 19 | // } else { 20 | // String level = args[0]; 21 | // if(level.equals("all")) { 22 | // Console.LOGGER.setLevel(Level.ALL); 23 | // Shell.write("switching to\n" 24 | // + "'all' level\n\n"); 25 | // } else if(level.equals("severe")) { 26 | // Console.LOGGER.setLevel(Level.SEVERE); 27 | // Shell.write("switching to\n" 28 | // + "'severe' level\n\n"); 29 | // } else { 30 | // Shell.write("Error:\n" 31 | // + "unrecognized level\n" 32 | // + "try 'all' or 'severe'\n\n"); 33 | // } 34 | // } 35 | 36 | openFile(); 37 | } 38 | 39 | private void openFile() { 40 | if(Desktop.isDesktopSupported()) { 41 | Desktop desktop = Desktop.getDesktop(); 42 | 43 | File file = new File(Console.logFile); 44 | if(!file.isFile()) { 45 | Console.die("Error:\n" 46 | + "log file not found"); 47 | return; 48 | } 49 | 50 | try { 51 | desktop.open(file); 52 | } catch(IOException e) { 53 | e.printStackTrace(); 54 | } 55 | } 56 | } 57 | 58 | // protected String getHelpMessage() { 59 | // return "'log'\n" 60 | // + "opens log file\n" 61 | // + "'log '\n" 62 | // + "changes log level\n" 63 | // + "'all': info and errors\n" 64 | // + "'severe': errors"; 65 | // } 66 | 67 | protected String getHelpMessage() { 68 | return "opens log file in\n" 69 | + "the default editor"; 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/vulc/luag/shell/command/ModeCommand.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.shell.command; 2 | 3 | import vulc.luag.Console; 4 | import vulc.luag.Console.Mode; 5 | import vulc.luag.shell.Shell; 6 | 7 | public class ModeCommand extends ShellCommand { 8 | 9 | public ModeCommand() { 10 | super("mode"); 11 | } 12 | 13 | public void run(String[] args) { 14 | if(args.length < 1) { 15 | Shell.write("current mode:\n"); 16 | if(Console.mode == Mode.DEVELOPER) { 17 | Shell.write("developer"); 18 | } else { 19 | Shell.write("user"); 20 | } 21 | Shell.write("\n\n"); 22 | return; 23 | } 24 | 25 | String mode = args[0]; 26 | if(mode.equals("d") || mode.equals("developer")) { 27 | Console.mode = Mode.DEVELOPER; 28 | Shell.write("switching to\n" 29 | + "developer mode\n\n"); 30 | } else if(mode.equals("u") || mode.equals("user")) { 31 | Console.mode = Mode.USER_SHELL; 32 | Shell.write("switching to\n" 33 | + "user mode\n\n"); 34 | } else { 35 | Console.die("Error:\n" 36 | + "unrecognized mode\n" 37 | + "try 'd' or 'u'"); 38 | } 39 | } 40 | 41 | protected String getHelpMessage() { 42 | return "'mode '\n" 43 | + "changes the console mode\n" 44 | + "'d' is developer\n" 45 | + "'u' is user"; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/vulc/luag/shell/command/PackCommand.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.shell.command; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.BufferedOutputStream; 5 | import java.io.File; 6 | import java.io.FileInputStream; 7 | import java.io.FileNotFoundException; 8 | import java.io.FileOutputStream; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.io.OutputStreamWriter; 12 | import java.util.zip.ZipEntry; 13 | import java.util.zip.ZipOutputStream; 14 | 15 | import com.google.gson.stream.JsonWriter; 16 | 17 | import vulc.luag.Console; 18 | import vulc.luag.game.Game; 19 | import vulc.luag.game.cartridge.Cartridge; 20 | import vulc.luag.game.interfaces.LuaInterface; 21 | 22 | public class PackCommand extends ShellCommand { 23 | 24 | public PackCommand() { 25 | super("pack"); 26 | isDevelopersOnly = true; 27 | } 28 | 29 | public void run(String[] args) { 30 | if(args.length < 1) { 31 | Console.die("Error: missing arguments\n" 32 | + "pack [cartridge-name]"); 33 | return; 34 | } 35 | 36 | File consoleUserdata = new File(Game.USERDATA_DIR); 37 | if(!consoleUserdata.isDirectory()) { 38 | Console.die("Error:\n" 39 | + "'" + Game.USERDATA_DIR_NAME + "'\n" 40 | + "folder not found"); 41 | return; 42 | } 43 | 44 | File cartridge = new File(Console.rootDirectory + args[0] + "." + Cartridge.EXTENSION); 45 | if(cartridge.exists()) { 46 | Console.die("Error:\n" 47 | + "'" + cartridge + "'\n" 48 | + "file already exists"); 49 | return; 50 | } 51 | 52 | synchronized(Console.DONT_STOP_LOCK) { 53 | try { 54 | ZipOutputStream out = null; 55 | boolean error = false; 56 | try { 57 | out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(cartridge))); 58 | cartridge.createNewFile(); 59 | 60 | if(!cartridge.exists()) error = true; 61 | } catch(FileNotFoundException e) { 62 | error = true; 63 | } 64 | if(error) { 65 | Console.die("Error:\n" 66 | + "could not\n" 67 | + "create cartridge"); 68 | return; 69 | } 70 | 71 | // add files inside 'console-userdata' 72 | File[] files = new File(Game.USERDATA_DIR).listFiles(); 73 | for(File f : files) { 74 | addToZip(out, "", f); 75 | } 76 | 77 | // add .cartridge-info file 78 | ZipEntry infoFile = new ZipEntry(Game.CARTRIDGE_INFO_NAME); 79 | out.putNextEntry(infoFile); 80 | { 81 | JsonWriter writer = new JsonWriter(new OutputStreamWriter(out)); 82 | writer.beginObject(); 83 | writer.name("console-version").value(Console.VERSION); 84 | writer.name("interface-version").value(LuaInterface.DEFAULT_MAJOR_VERSION 85 | + "." 86 | + LuaInterface.minorVersion(LuaInterface.DEFAULT_MAJOR_VERSION)); 87 | writer.endObject(); 88 | 89 | writer.flush(); 90 | } 91 | out.closeEntry(); 92 | 93 | out.close(); 94 | } catch(IOException e) { 95 | e.printStackTrace(); 96 | } 97 | } 98 | } 99 | 100 | private void addToZip(ZipOutputStream zip, String folder, File file) throws IOException { 101 | if(file.isFile()) { 102 | ZipEntry entry = new ZipEntry(folder + file.getName()); 103 | zip.putNextEntry(entry); 104 | 105 | InputStream in = new BufferedInputStream(new FileInputStream(file)); 106 | byte[] dataBuffer = new byte[1024]; 107 | int lengthRead; 108 | 109 | while((lengthRead = in.read(dataBuffer)) >= 0) { 110 | zip.write(dataBuffer, 0, lengthRead); 111 | zip.flush(); 112 | } 113 | in.close(); 114 | 115 | zip.closeEntry(); 116 | } else if(file.isDirectory()) { 117 | File[] files = file.listFiles(); 118 | for(File f : files) { 119 | addToZip(zip, folder + file.getName() + "/", f); 120 | } 121 | } 122 | } 123 | 124 | protected String getHelpMessage() { 125 | return "'pack '\n" 126 | + "creates a cartridge"; 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /src/vulc/luag/shell/command/RunCommand.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.shell.command; 2 | 3 | import vulc.luag.Console; 4 | import vulc.luag.Console.Mode; 5 | import vulc.luag.game.cartridge.Cartridge; 6 | import vulc.luag.gfx.panel.GamePanel; 7 | 8 | public class RunCommand extends ShellCommand { 9 | 10 | public RunCommand() { 11 | super("run"); 12 | } 13 | 14 | public void run(String[] args) { 15 | if(args.length >= 1) { 16 | Console.cartridge = Console.rootDirectory + args[0] + "." + Cartridge.EXTENSION; 17 | } else { 18 | Console.cartridge = null; 19 | 20 | if(Console.mode == Mode.USER_SHELL) { 21 | Console.die("Error:\n" 22 | + "insert cartridge name"); 23 | return; 24 | } 25 | } 26 | Console.switchToPanel(new GamePanel()); 27 | } 28 | 29 | protected String getHelpMessage() { 30 | if(Console.mode == Mode.DEVELOPER) { 31 | return "'run'\n" 32 | + "runs the developed game\n" 33 | + "'run '\n" 34 | + "runs the cartridge"; 35 | } else { 36 | return "'run '\n" 37 | + "runs the cartridge"; 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/vulc/luag/shell/command/SetupCommand.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.shell.command; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.BufferedOutputStream; 5 | import java.io.File; 6 | import java.io.FileOutputStream; 7 | import java.io.IOException; 8 | import java.io.OutputStream; 9 | import java.util.zip.ZipEntry; 10 | import java.util.zip.ZipInputStream; 11 | 12 | import vulc.luag.Console; 13 | import vulc.luag.game.Game; 14 | 15 | public class SetupCommand extends ShellCommand { 16 | 17 | private static final String TEMPLATE_FILE = "/res/templates/template.zip"; 18 | 19 | public SetupCommand() { 20 | super("setup"); 21 | isDevelopersOnly = true; 22 | } 23 | 24 | public void run(String[] args) { 25 | File folder = new File(Game.USERDATA_DIR); 26 | if(folder.exists()) { 27 | Console.die("Error:\n" 28 | + "'" + Game.USERDATA_DIR_NAME + "'\n" 29 | + "already exists"); 30 | return; 31 | } 32 | 33 | synchronized(Console.DONT_STOP_LOCK) { 34 | try { 35 | new File(Game.USERDATA_DIR).mkdir(); 36 | 37 | ZipInputStream template = 38 | new ZipInputStream(new BufferedInputStream(SetupCommand.class.getResourceAsStream(TEMPLATE_FILE))); 39 | 40 | ZipEntry entry; 41 | byte[] buffer = new byte[1024]; 42 | 43 | while((entry = template.getNextEntry()) != null) { 44 | File file = new File(Game.USERDATA_DIR + "/" + entry.getName()); 45 | if(entry.isDirectory()) { 46 | file.mkdirs(); 47 | } else { 48 | OutputStream out = new BufferedOutputStream(new FileOutputStream(file)); 49 | 50 | int readLength; 51 | while((readLength = template.read(buffer)) > 0) { 52 | out.write(buffer, 0, readLength); 53 | } 54 | out.close(); 55 | } 56 | } 57 | template.closeEntry(); 58 | template.close(); 59 | } catch(IOException e) { 60 | e.printStackTrace(); 61 | } 62 | } 63 | } 64 | 65 | protected String getHelpMessage() { 66 | return "creates blank game files"; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/vulc/luag/shell/command/ShellCommand.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.shell.command; 2 | 3 | import java.util.Arrays; 4 | 5 | import vulc.luag.Console; 6 | import vulc.luag.Console.Mode; 7 | 8 | public abstract class ShellCommand { 9 | 10 | private static final ShellCommand[] COMMAND_LIST = { 11 | new RunCommand(), 12 | new EditCommand(), 13 | new PackCommand(), 14 | new SetupCommand(), 15 | new ClsCommand(), 16 | new VerCommand(), 17 | new HelpCommand(), 18 | new ModeCommand(), 19 | new FilesCommand(), 20 | new LogCommand(), 21 | new ExitCommand() 22 | }; 23 | 24 | private final String[] names; 25 | protected boolean isDevelopersOnly = false; 26 | 27 | public ShellCommand(String... names) { 28 | this.names = names; 29 | } 30 | 31 | protected abstract void run(String[] args); 32 | 33 | protected String getHelpMessage() { 34 | return "this command has no\n" 35 | + "'help' message"; 36 | } 37 | 38 | // returns true if could find a command, else false 39 | public static boolean execute(String line) { 40 | Console.LOGGER.info("Shell execute: '" + line + "'"); 41 | 42 | String[] splittedLine = line.split(" "); 43 | 44 | String name = splittedLine[0]; 45 | String[] args = Arrays.copyOfRange(splittedLine, 1, splittedLine.length); 46 | 47 | ShellCommand command = findCommand(name); 48 | if(command != null) { 49 | if(command.isDevelopersOnly && Console.mode != Mode.DEVELOPER) { 50 | Console.die("Error:\n" 51 | + "only developers can\n" 52 | + "use this command"); 53 | } else { 54 | command.run(args); 55 | } 56 | return true; 57 | } 58 | return false; 59 | } 60 | 61 | protected static ShellCommand findCommand(String name) { 62 | for(ShellCommand command : COMMAND_LIST) { 63 | for(int i = 0; i < command.names.length; i++) { 64 | if(name.equalsIgnoreCase(command.names[i])) return command; 65 | } 66 | } 67 | return null; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/vulc/luag/shell/command/VerCommand.java: -------------------------------------------------------------------------------- 1 | package vulc.luag.shell.command; 2 | 3 | import vulc.luag.Console; 4 | import vulc.luag.shell.Shell; 5 | 6 | public class VerCommand extends ShellCommand { 7 | 8 | public VerCommand() { 9 | super("ver", "version"); 10 | } 11 | 12 | public void run(String[] args) { 13 | Shell.write(Console.VERSION + " - By Vulcalien\n\n"); 14 | } 15 | 16 | protected String getHelpMessage() { 17 | return "prints version and author"; 18 | } 19 | 20 | } 21 | --------------------------------------------------------------------------------