diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..36efe2a29 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,16 @@ +name: Java CI + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Build with Gradle + run: ./gradlew build \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index d6e5d91c5..f83f00e8d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: Java - jdk: - - oraclejdk8 + - openjdk8 + diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b9f60d9d..d6c531ef7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,90 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [4.0.2] +- Controller (gamepad) support. +- .aab package generation for Android. +- In panel showing choose options, up/down buttons now have animation. +- Update libgdx to latest version 1.9.13. +- Better Ink text extraction for I18N. +- Fix: Bug deleting animation if initial animation is not set. + +## [4.0.1] +- Fix error serializing cb when Ink library is not used. +- Fix error creating resolution when there are atlases with subfolders. +- Fix Android launcher to make expansion files (.obb) works again. + +## [4.0.0] +- Fix error when android keystore path had spaces. +- Added scene counter in the editor ui. +- Updated ios minosversion to 8. +- Better calc of speed when walking and fake depth is used. + +## [3.2.5] +- Some validations adding/editing animations and verbs to avoid errors. +- Fixed dealing with OpenAL bug in credits screen. +- Fixed bug extracting ink texts. + +## [3.2.4] +- Added more external functions for Ink. +- Added support to load/save game preferences. +- Scale and Rotate actions now work with ui actors. +- Many bugs fixed. + +## [3.2.3] +- Added "initVerb" param to the "Leave" action. This verb will be executed if set instead of the "init" verb. +- Now, the "init" verb doesn't run if the "test" verb is executed. +- Load/Save preferences support. +- Allow to move BaseActors in MoveToSceneAction. +- FIX: Scale and Rotate actions didn't work with ui actors. +- Save also callbacks that are not in the current scene. + +## [3.2.2] +- Inventory button style now in the InventoryUI style. This allows to customize the inventory button by player. +- Update Blade Ink to v0.7.3 which fixes an important bug. +- A lot of bugs fixed (see git log). + +## [3.2.1] + +- Added bubble positioning parameters on ui.json. +- Better multiple inventory support in actions. +- Better Spine skin handling. +- Update Blade Ink to v0.7.2. +- A lot of bugs fixed (see git log). + +## [3.2.0] + +- Say Action: Talk animation also for text types PLAIN and SUBTITLE. +- Only save modified actor properties on savegames. +- New SetDesc action to change the actor descriptions. +- Added 'show_hotspots' config key to enable/disable the show hotspots feature. +- Added INSIDE property to IfAttrProperty action to check if an actor is inside other actor. +- Improve size of chapter files and savegames. +- Updated libgdx to v1.9.10 +- Updated Spine to v3.8. +- Update gradle to v5.4.1 + +## [3.1.2] + +- Animated ui icons and cursors. +- Update RoboVM to v2.3.7. +- Don't extract string expresions from ink files for i18n. +- Allow empty values in Property action. +- Added public methods to the SpineRenderer to get access to the current skeleton and animation. + +## [3.1.1] + +- Dialog to create the android keystore inside the editor. +- Label and text to warn about legacy dialogs. +- Added RandomPosition action. +- Added reload assets icon to scene list. +- Added support for combining skins in Spine. +- Delete last_project key if loadproject fails so the editor doesn't fail forever. +- Delete SetModelProp action and supporting library. It was not useful and make porting dificult. +- Added IN_UI if property. +- Fix: Now search in all inventories. +- Fix: Set skin now updates properly. + ## [3.1.0] - Update Spine runtime to v3.7 diff --git a/README.md b/README.md index fa8933cbc..cfc8adfab 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,6 @@ The **Bladecoder Adventure Engine** has been developed using the [LibGDX](http:/ ### Adventure Editor The **Adventure Editor** is a graphical editor to create full point and click games with minimal programming. -Download on Flathub - ![adventure editor 2014-09-26](https://cloud.githubusercontent.com/assets/6229260/4420346/1d3a1b8a-4578-11e4-8eec-415f5e27c005.png) ### Blade Engine @@ -41,11 +39,15 @@ The source of **The Goddess Robbery** can be downloaded [here](https://github.c ### Documentation -All available documentation is in the [wiki page](https://github.com/bladecoder/bladecoder-adventure-engine/wiki). The documentation is not good and needs to improve, we are working on it. Meanwhile you can download and look into the [test projects](https://github.com/bladecoder/bladecoder-adventure-tests/). +All available documentation is in the [wiki page](https://github.com/bladecoder/bladecoder-adventure-engine/wiki). The documentation is not good enough and needs to improve, we are working on it. Meanwhile you can download and look into the [test projects](https://github.com/bladecoder/bladecoder-adventure-tests/). ### Download latest release -Check the [release page](https://github.com/bladecoder/bladecoder-adventure-engine/releases) to download the latest version. +Check the [release page](https://github.com/bladecoder/bladecoder-adventure-engine/releases/latest) to download the latest version. + +For Linux users, there is a Flatpack package with all the dependencies included. + +Download on Flathub ### Building and running In order to compile, build and run the engine, the Java platform is necessary. The project uses Gradle to build and package. diff --git a/adventure-editor/assets-raw/editor/editor-icon.svg b/adventure-editor/assets-raw/editor/editor-icon.svg index 04fc0b027..34bc16e43 100644 --- a/adventure-editor/assets-raw/editor/editor-icon.svg +++ b/adventure-editor/assets-raw/editor/editor-icon.svg @@ -7,33 +7,19 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="128" height="128" id="svg2" version="1.1" - inkscape:version="0.48+devel r" - sodipodi:docname="ic_app.svg" + inkscape:version="0.92.4 (5da689c313, 2019-01-14)" + sodipodi:docname="editor-icon.svg" inkscape:export-filename="ic_app16.png" inkscape:export-xdpi="11.25" inkscape:export-ydpi="11.25"> - - - - + id="defs4" /> - + transform="translate(1891.1343,1558.0345)"> + + + + + + + + diff --git a/adventure-editor/assets-raw/editor/icons/ic_reload_small.png b/adventure-editor/assets-raw/editor/icons/ic_reload_small.png new file mode 100644 index 000000000..549a03cc2 Binary files /dev/null and b/adventure-editor/assets-raw/editor/icons/ic_reload_small.png differ diff --git a/adventure-editor/assets-raw/editor/icons/ic_reload_small_disabled.png b/adventure-editor/assets-raw/editor/icons/ic_reload_small_disabled.png new file mode 100644 index 000000000..f88e20a35 Binary files /dev/null and b/adventure-editor/assets-raw/editor/icons/ic_reload_small_disabled.png differ diff --git a/adventure-editor/assets-raw/editor/icons/gen.sh b/adventure-editor/assets-raw/editor/pack_icons.sh similarity index 50% rename from adventure-editor/assets-raw/editor/icons/gen.sh rename to adventure-editor/assets-raw/editor/pack_icons.sh index fd28b8b0c..7523e59d1 100755 --- a/adventure-editor/assets-raw/editor/icons/gen.sh +++ b/adventure-editor/assets-raw/editor/pack_icons.sh @@ -1,7 +1,10 @@ #!/bin/sh -VERSION=1.6.5 -LIBGDX_BASE_PATH=~/.gradle/caches/modules-2/files-2.1/com.badlogicgames.gdx/ +VERSION=1.9.9 +LIBGDX_BASE_PATH=$HOME/.gradle/caches/modules-2/files-2.1/com.badlogicgames.gdx/ GDX_PATH=`find $LIBGDX_BASE_PATH -name gdx-$VERSION.jar` GDX_TOOLS_PATH=`find $LIBGDX_BASE_PATH -name gdx-tools-$VERSION.jar` +OUT_DIR=../../src/main/resources/images + +cp pack.json $target +java -cp $GDX_PATH:$GDX_TOOLS_PATH com.badlogic.gdx.tools.texturepacker.TexturePacker icons $OUT_DIR icons -java -cp $GDX_PATH:$GDX_TOOLS_PATH com.badlogic.gdx.tools.texturepacker.TexturePacker images ../../../src/main/resources/images icons diff --git a/adventure-editor/assets-raw/editor/title.svg b/adventure-editor/assets-raw/editor/title.svg index 6a4afed4a..b639747cb 100644 --- a/adventure-editor/assets-raw/editor/title.svg +++ b/adventure-editor/assets-raw/editor/title.svg @@ -13,7 +13,7 @@ height="53.66" id="svg2" version="1.1" - inkscape:version="0.91+devel r" + inkscape:version="0.92.4 (5da689c313, 2019-01-14)" sodipodi:docname="title.svg" inkscape:export-filename="images/title.png" inkscape:export-xdpi="108.63792" @@ -29,17 +29,17 @@ inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:zoom="1.4" - inkscape:cx="24.483251" + inkscape:cx="36.626108" inkscape:cy="-71.40974" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="false" showborder="true" borderlayer="true" - inkscape:window-width="1215" - inkscape:window-height="1000" - inkscape:window-x="1431" - inkscape:window-y="24" + inkscape:window-width="1920" + inkscape:window-height="1442" + inkscape:window-x="-11" + inkscape:window-y="-11" inkscape:window-maximized="1" inkscape:snap-global="false" fit-margin-top="0" @@ -80,11 +80,10 @@ transform="translate(0.66431808,-999.6792)"> AdventureCOMPOSER - - - - + id="defs4" /> + inkscape:snap-global="false" + inkscape:pagecheckerboard="true" /> @@ -72,24 +59,13 @@ inkscape:groupmode="layer" id="layer1" transform="translate(0,-924.3622)"> - + style="opacity:1;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect816" + width="156.07143" + height="156.07143" + x="-16.785715" + y="912.00507" /> params) { } try { - return ActionFactory.createByClass(c.getName(), params); + return ActionFactory.create(c.getName(), params); } catch (ClassNotFoundException | ReflectionException e) { EditorLogger.error("Action with name '" + name + "' not found."); diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/AlignUtils.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/AlignUtils.java index 655a10304..195e6fdc4 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/AlignUtils.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/AlignUtils.java @@ -3,13 +3,13 @@ import com.badlogic.gdx.utils.Align; public class AlignUtils { - + public static String getAlign(int align) { - switch(align) { + switch (align) { case Align.bottomRight: - return "botton-right"; + return "bottom-right"; case Align.bottomLeft: - return "botton-left"; + return "bottom-left"; case Align.topRight: return "top-right"; case Align.topLeft: @@ -19,36 +19,36 @@ public static String getAlign(int align) { case Align.left: return "left"; case Align.bottom: - return "botton"; + return "bottom"; case Align.top: return "top"; case Align.center: return "center"; } - + return ""; } - + public static int getAlign(String s) { - if("botton-right".equals(s)) + if ("bottom-right".equals(s)) return Align.bottomRight; - else if("botton-left".equals(s)) + else if ("bottom-left".equals(s)) return Align.bottomLeft; - else if("top-right".equals(s)) + else if ("top-right".equals(s)) return Align.topRight; - else if("top-left".equals(s)) + else if ("top-left".equals(s)) return Align.topLeft; - else if("right".equals(s)) + else if ("right".equals(s)) return Align.right; - else if("left".equals(s)) + else if ("left".equals(s)) return Align.left; - else if("botton".equals(s)) + else if ("bottom".equals(s)) return Align.bottom; - else if("top".equals(s)) + else if ("top".equals(s)) return Align.top; - else if("center".equals(s)) + else if ("center".equals(s)) return Align.center; - + return 0; } } diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/CustomTextureUnpacker.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/CustomTextureUnpacker.java index 051bf9094..b6b95eabc 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/CustomTextureUnpacker.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/CustomTextureUnpacker.java @@ -23,7 +23,6 @@ public class CustomTextureUnpacker { private static final int NINEPATCH_PADDING = 1; private static final String OUTPUT_TYPE = "png"; - /** Splits an atlas into seperate image and ninepatch files. */ public void splitAtlas(TextureAtlasData atlas, String outputDir) { // create the output directory if it did not exist yet @@ -42,8 +41,9 @@ public void splitAtlas(TextureAtlasData atlas, String outputDir) { printExceptionAndExit(e); } for (Region region : atlas.getRegions()) { - EditorLogger.debug(String.format("Processing image for %s(%s): x[%s] y[%s] w[%s] h[%s], rotate[%s]", - region.name, region.index, region.left, region.top, region.width, region.height, region.rotate)); + EditorLogger.debug( + String.format("Processing image for %s(%s): x[%s] y[%s] w[%s] h[%s], rotate[%s]", region.name, + region.index, region.left, region.top, region.width, region.height, region.rotate)); // check if the page this region is in is currently loaded in a // Buffered Image @@ -53,7 +53,7 @@ public void splitAtlas(TextureAtlasData atlas, String outputDir) { // check if the region is a ninepatch or a normal image and // delegate accordingly - if (region.splits == null) { + if (region.findValue("split") == null) { splitImage = extractImage(img, region, outputDirFile, 0); extension = OUTPUT_TYPE; } else { @@ -63,7 +63,8 @@ public void splitAtlas(TextureAtlasData atlas, String outputDir) { // check if the parent directories of this image file exist // and create them if not - File imgOutput = new File(outputDirFile, String.format("%s.%s", region.index == -1?region.name:region.name + "_" + region.index, extension)); + File imgOutput = new File(outputDirFile, String.format("%s.%s", + region.index == -1 ? region.name : region.name + "_" + region.index, extension)); File imgDir = imgOutput.getParentFile(); if (!imgDir.exists()) { System.out.println(String.format("Creating directory: %s", imgDir.getPath())); @@ -84,14 +85,10 @@ public void splitAtlas(TextureAtlasData atlas, String outputDir) { /** * Extract an image from a texture atlas. * - * @param page - * The image file related to the page the region is in - * @param region - * The region to extract - * @param outputDirFile - * The output directory - * @param padding - * padding (in pixels) to apply to the image + * @param page The image file related to the page the region is in + * @param region The region to extract + * @param outputDirFile The output directory + * @param padding padding (in pixels) to apply to the image * @return The extracted image */ private BufferedImage extractImage(BufferedImage page, Region region, File outputDirFile, int padding) { @@ -113,19 +110,20 @@ private BufferedImage extractImage(BufferedImage page, Region region, File outpu // draw the image to a bigger one if padding is needed if (padding > 0) { - BufferedImage paddedImage = new BufferedImage(splitImage.getWidth() + padding * 2, splitImage.getHeight() - + padding * 2, page.getType()); + BufferedImage paddedImage = new BufferedImage(splitImage.getWidth() + padding * 2, + splitImage.getHeight() + padding * 2, page.getType()); Graphics2D g2 = paddedImage.createGraphics(); g2.drawImage(splitImage, padding, padding, null); g2.dispose(); return paddedImage; } else if (region.originalWidth != region.width || region.originalHeight != region.height) { - BufferedImage paddedImage = new BufferedImage(region.originalWidth, region.originalHeight, page.getType()); - Graphics2D g2 = paddedImage.createGraphics(); + BufferedImage paddedImage = new BufferedImage(region.originalWidth, region.originalHeight, page.getType()); + Graphics2D g2 = paddedImage.createGraphics(); // g2.drawImage(splitImage, (int)region.offsetX, region.originalHeight - region.height, null); - g2.drawImage(splitImage, (int)region.offsetX, region.originalHeight - region.height - (int)region.offsetY, null); - g2.dispose(); - return paddedImage; + g2.drawImage(splitImage, (int) region.offsetX, region.originalHeight - region.height - (int) region.offsetY, + null); + g2.dispose(); + return paddedImage; } else { return splitImage; } @@ -135,13 +133,11 @@ private BufferedImage extractImage(BufferedImage page, Region region, File outpu * Extract a ninepatch from a texture atlas, according to the android * specification. * - * @see ninepatch + * @see ninepatch * specification - * @param page - * The image file related to the page the region is in - * @param region - * The region to extract + * @param page The image file related to the page the region is in + * @param region The region to extract */ private BufferedImage extractNinePatch(BufferedImage page, Region region, File outputDirFile) { BufferedImage splitImage = extractImage(page, region, outputDirFile, NINEPATCH_PADDING); @@ -149,19 +145,21 @@ private BufferedImage extractNinePatch(BufferedImage page, Region region, File o g2.setColor(Color.BLACK); // Draw the four lines to save the ninepatch's padding and splits - int startX = region.splits[0] + NINEPATCH_PADDING; - int endX = region.width - region.splits[1] + NINEPATCH_PADDING - 1; - int startY = region.splits[2] + NINEPATCH_PADDING; - int endY = region.height - region.splits[3] + NINEPATCH_PADDING - 1; + int[] splits = region.findValue("split"); + int startX = splits[0] + NINEPATCH_PADDING; + int endX = region.width - splits[1] + NINEPATCH_PADDING - 1; + int startY = splits[2] + NINEPATCH_PADDING; + int endY = region.height - splits[3] + NINEPATCH_PADDING - 1; if (endX >= startX) g2.drawLine(startX, 0, endX, 0); if (endY >= startY) g2.drawLine(0, startY, 0, endY); - if (region.pads != null) { - int padStartX = region.pads[0] + NINEPATCH_PADDING; - int padEndX = region.width - region.pads[1] + NINEPATCH_PADDING - 1; - int padStartY = region.pads[2] + NINEPATCH_PADDING; - int padEndY = region.height - region.pads[3] + NINEPATCH_PADDING - 1; + int[] pads = region.findValue("pad"); + if (pads != null) { + int padStartX = pads[0] + NINEPATCH_PADDING; + int padEndX = region.width - pads[1] + NINEPATCH_PADDING - 1; + int padStartY = pads[2] + NINEPATCH_PADDING; + int padEndY = region.height - pads[3] + NINEPATCH_PADDING - 1; g2.drawLine(padStartX, splitImage.getHeight() - 1, padEndX, splitImage.getHeight() - 1); g2.drawLine(splitImage.getWidth() - 1, padStartY, splitImage.getWidth() - 1, padEndY); } diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/DesktopUtils.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/DesktopUtils.java index 31a1c6729..7a727d7cb 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/DesktopUtils.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/DesktopUtils.java @@ -34,8 +34,7 @@ public class DesktopUtils { public static void browse(Component parent, String uri) { boolean error = false; - if (Desktop.isDesktopSupported() - && Desktop.getDesktop().isSupported(Action.BROWSE)) { + if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Action.BROWSE)) { try { Desktop.getDesktop().browse(new URI(uri)); } catch (URISyntaxException ex) { @@ -68,8 +67,13 @@ public static void removeDir(String dir) throws IOException { File files[] = f.listFiles(); if (files != null) - for (File f2 : files) - Files.delete(f2.toPath()); + for (File f2 : files) { + if (f2.isDirectory()) { + removeDir(f2.getAbsolutePath()); + } else { + Files.delete(f2.toPath()); + } + } Files.deleteIfExists(f.toPath()); } diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/EditorCommandExecutor.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/EditorCommandExecutor.java index 3be6e2474..10a7dfeb7 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/EditorCommandExecutor.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/EditorCommandExecutor.java @@ -38,7 +38,12 @@ public void debug(boolean value) { public void extractDialogs() { ModelTools.extractDialogs(); - EditorLogger.msg("PROCCESS FINISHED."); + EditorLogger.msg("ExtractDialogs FINISHED."); + } + + public void cleanI18N() { + Ctx.project.getI18N().cleanI18N(); + EditorLogger.msg("CleanI18N FINISHED."); } public void printUnusedSounds() { diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/I18NUtils.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/I18NUtils.java index 3ac88c1bc..4cb42ba67 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/I18NUtils.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/I18NUtils.java @@ -43,10 +43,10 @@ public class I18NUtils { public static final void exportTSV(String modelPath, String outFile, final String chapterId, String defaultLocale) throws FileNotFoundException, IOException { File defaultChapter = new File(modelPath, chapterId + PROPERTIES_EXT); - + File outputFile; - - if(outFile == null) + + if (outFile == null) outputFile = new File(modelPath, chapterId + TSV_EXT); else outputFile = new File(outFile); @@ -63,9 +63,9 @@ public boolean accept(File arg0, String arg1) { }); OrderedProperties props[] = new OrderedProperties[files.length + 1]; - + props[0] = new OrderedPropertiesBuilder().withSuppressDateInComment(true).withOrdering().build(); - + props[0].load(new InputStreamReader(new FileInputStream(defaultChapter), I18N.ENCODING)); for (int i = 1; i < props.length; i++) { @@ -92,14 +92,14 @@ public boolean accept(File arg0, String arg1) { } writer.write("\n"); - + Set> keySet = props[0].entrySet(); for (Entry e : keySet) { writer.write(e.getKey()); for (OrderedProperties p : props) { - if(p.getProperty(e.getKey()) == null) { + if (p.getProperty(e.getKey()) == null) { writer.write(SEPARATOR + "**" + props[0].getProperty(e.getKey()).replace("\n", "\\n")); System.out.println("KEY NOT FOUND: " + e); } else { @@ -134,19 +134,19 @@ public static final void importTSV(String modelPath, String tsvFile, String chap // get keys and texts while ((line = br.readLine()) != null) { String[] values = line.split(SEPARATOR); - - if(values.length != langs.length) { + + if (values.length != langs.length) { EditorLogger.error("Incorrect line in .tsv: " + line); continue; } - + String key = values[0]; for (int i = 0; i < props.length; i++) { String value = values[i + 1]; - if(value != null) + if (value != null) value = value.replace("\\n", "\n"); - + props[i].setProperty(key, value); } } @@ -170,18 +170,20 @@ public static final void importTSV(String modelPath, String tsvFile, String chap } } - public static final void newLocale(String modelPath, final String chapterId, String defaultLocale, - String newLocale) throws FileNotFoundException, IOException { + public static final void newLocale(String modelPath, final String chapterId, String defaultLocale, String newLocale) + throws FileNotFoundException, IOException { File defaultChapter = new File(modelPath, chapterId + PROPERTIES_EXT); File newChapter = new File(modelPath, chapterId + "_" + newLocale + PROPERTIES_EXT); - OrderedProperties defaultProp = new OrderedPropertiesBuilder().withSuppressDateInComment(true).withOrdering().build(); - OrderedProperties newProp = new OrderedPropertiesBuilder().withSuppressDateInComment(true).withOrdering().build(); + OrderedProperties defaultProp = new OrderedPropertiesBuilder().withSuppressDateInComment(true).withOrdering() + .build(); + OrderedProperties newProp = new OrderedPropertiesBuilder().withSuppressDateInComment(true).withOrdering() + .build(); defaultProp.load(new InputStreamReader(new FileInputStream(defaultChapter), I18N.ENCODING)); for (Entry e : defaultProp.entrySet()) { - newProp.setProperty(e.getKey(), "**" + (String) defaultProp.getProperty(e.getKey())); + newProp.setProperty(e.getKey(), "**" + defaultProp.getProperty(e.getKey())); } // save new .properties @@ -189,73 +191,79 @@ public static final void newLocale(String modelPath, final String chapterId, Str Writer out = new OutputStreamWriter(os, I18N.ENCODING); newProp.store(out, newChapter.getName()); } - - public static final void compare(String modelPath, final String chapterId, String defaultLocale, - String destLocale) throws FileNotFoundException, IOException { + + public static final void compare(String modelPath, final String chapterId, String defaultLocale, String destLocale) + throws FileNotFoundException, IOException { File defaultChapter = new File(modelPath, chapterId + PROPERTIES_EXT); File destChapter = new File(modelPath, chapterId + "_" + destLocale + PROPERTIES_EXT); - OrderedProperties defaultProp = new OrderedPropertiesBuilder().withSuppressDateInComment(true).withOrdering().build(); - OrderedProperties destProp = new OrderedPropertiesBuilder().withSuppressDateInComment(true).withOrdering().build(); + OrderedProperties defaultProp = new OrderedPropertiesBuilder().withSuppressDateInComment(true).withOrdering() + .build(); + OrderedProperties destProp = new OrderedPropertiesBuilder().withSuppressDateInComment(true).withOrdering() + .build(); defaultProp.load(new InputStreamReader(new FileInputStream(defaultChapter), I18N.ENCODING)); destProp.load(new InputStreamReader(new FileInputStream(destChapter), I18N.ENCODING)); // SEARCH FOR NOT EXISTING DEST KEYS for (Entry e : defaultProp.entrySet()) { - if(destProp.getProperty(e.getKey()) == null) { + if (destProp.getProperty(e.getKey()) == null) { EditorLogger.error("Key not found in '" + destLocale + "' locale: " + e.getKey()); } } - + // SEARCH FOR NOT EXISTING DEFAULT CHAPTER KEYS for (Entry e : destProp.entrySet()) { - if(defaultProp.getProperty(e.getKey()) == null) { + if (defaultProp.getProperty(e.getKey()) == null) { EditorLogger.error("Key not found in default locale: " + e.getKey()); } } } - - public static final void sync(String modelPath, final String chapterId, String defaultLocale, - String destLocale) throws FileNotFoundException, IOException { + + public static final void sync(String modelPath, final String chapterId, String defaultLocale, String destLocale) + throws FileNotFoundException, IOException { File defaultChapter = new File(modelPath, chapterId + PROPERTIES_EXT); File destChapter = new File(modelPath, chapterId + "_" + destLocale + PROPERTIES_EXT); - OrderedProperties defaultProp = new OrderedPropertiesBuilder().withSuppressDateInComment(true).withOrdering().build(); - OrderedProperties destProp = new OrderedPropertiesBuilder().withSuppressDateInComment(true).withOrdering().build(); + OrderedProperties defaultProp = new OrderedPropertiesBuilder().withSuppressDateInComment(true).withOrdering() + .build(); + OrderedProperties destProp = new OrderedPropertiesBuilder().withSuppressDateInComment(true).withOrdering() + .build(); defaultProp.load(new InputStreamReader(new FileInputStream(defaultChapter), I18N.ENCODING)); destProp.load(new InputStreamReader(new FileInputStream(destChapter), I18N.ENCODING)); // SEARCH FOR NOT EXISTING DEST KEYS for (String key : defaultProp.stringPropertyNames()) { - if(destProp.getProperty(key) == null) { - System.out.println("ADDING Key not found in '" + destLocale + "' locale: " + key + "=" + defaultProp.getProperty(key)); + if (destProp.getProperty(key) == null) { + System.out.println("ADDING Key not found in '" + destLocale + "' locale: " + key + "=" + + defaultProp.getProperty(key)); destProp.setProperty(key, "**" + defaultProp.getProperty(key)); } } - + // SEARCH FOR NOT EXISTING DEFAULT CHAPTER KEYS for (String key : destProp.stringPropertyNames()) { - if(defaultProp.getProperty(key) == null) { + if (defaultProp.getProperty(key) == null) { System.out.println("DELETE MANUALLY Key not found in default locale: " + key); } } - + // save dest .properties FileOutputStream os = new FileOutputStream(destChapter); Writer out = new OutputStreamWriter(os, I18N.ENCODING); destProp.store(out, destChapter.getName()); } - public static final String translatePhrase(String phrase, String sourceLangCode, String destLangCode) throws UnsupportedEncodingException { + public static final String translatePhrase(String phrase, String sourceLangCode, String destLangCode) + throws UnsupportedEncodingException { // String query = MessageFormat.format(GOOGLE_TRANSLATE_URL, phrase, // sourceLangCode, destLangCode); // String query = GOOGLE_TRANSLATE_URL + "?q=" + phrase + "&source=" + sourceLangCode + "&target=" + destLangCode // + "&key=" + GOOGLE_API_KEY; - - String query = "https://translate.googleapis.com/translate_a/single?client=gtx&sl=" - + sourceLangCode + "&tl=" + destLangCode + "&dt=t&q=" + URLEncoder.encode(phrase, "UTF-8"); + + String query = "https://translate.googleapis.com/translate_a/single?client=gtx&sl=" + sourceLangCode + "&tl=" + + destLangCode + "&dt=t&q=" + URLEncoder.encode(phrase, "UTF-8"); System.out.println(query); String result = HttpUtils.excuteHTTP(query, null); diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/ImageUtils.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/ImageUtils.java index 1144b3a91..bba0989ba 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/ImageUtils.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/ImageUtils.java @@ -138,7 +138,7 @@ public static void scaleAtlas(File orgAtlas, File destDir, float scale) throws I int maxWH = (int) (getRecommendedAtlasSize() * scale); createAtlas(tmpDir.getAbsolutePath(), destDir.getAbsolutePath(), orgAtlas.getName(), scale, maxWH, maxWH, - atlasData.getPages().get(0).minFilter, atlasData.getPages().get(0).magFilter, outputFormat); + atlasData.getPages().get(0).minFilter, atlasData.getPages().get(0).magFilter, outputFormat, true); DesktopUtils.removeDir(tmpDir.getAbsolutePath()); } @@ -174,7 +174,8 @@ public static int getRecommendedAtlasSize() { } public static void createAtlas(String inDir, String outdir, String name, float scale, int maxWidth, int maxHeight, - TextureFilter filterMin, TextureFilter filterMag, String outputFormat) throws IOException { + TextureFilter filterMin, TextureFilter filterMag, String outputFormat, boolean stripWhiteSpace) + throws IOException { Settings settings = new Settings(); settings.pot = false; @@ -185,8 +186,8 @@ public static void createAtlas(String inDir, String outdir, String name, float s settings.rotation = false; settings.minWidth = 16; settings.minWidth = 16; - settings.stripWhitespaceX = true; - settings.stripWhitespaceY = true; + settings.stripWhitespaceX = stripWhiteSpace; + settings.stripWhitespaceY = stripWhiteSpace; settings.alphaThreshold = 0; settings.filterMin = filterMin; diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/ModelTools.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/ModelTools.java index 16041c889..9eebe558b 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/ModelTools.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/ModelTools.java @@ -46,6 +46,7 @@ import com.bladecoder.engine.actions.SetCutmodeAction; import com.bladecoder.engine.assets.EngineAssetManager; import com.bladecoder.engine.i18n.I18N; +import com.bladecoder.engine.ink.InkManager; import com.bladecoder.engine.model.BaseActor; import com.bladecoder.engine.model.CharacterActor; import com.bladecoder.engine.model.Dialog; @@ -499,10 +500,36 @@ else if (v.name.equals("s")) for (int i = 0; i < v.size; i++) { JsonValue aValue = v.get(i); + // Ignore string declr ej. "xxx" + if (i > 0 && v.get(i - 1).isString() && v.get(i - 1).asString().equals("str")) { + // check if inside a choice with [xxx] text. + + // comparison == or !=? + if (v.size > i + 2 && v.get(i + 2).isString() + && (v.get(i + 2).asString().equals("==") || v.get(i + 2).asString().equals("!="))) + continue; + + // find "/ev" + boolean ev = false; + int j = i + 2; + while (j < v.size && !ev) { + JsonValue next = v.get(j); + if (!next.isObject() && next.asString().equals("/ev")) { + ev = true; + } + + j++; + } + + JsonValue next = v.get(j); + if (!next.isObject() || next.get("*") == null) + continue; + } + extractInkTextsInternal(aValue, sbTSV, sbMD, prop); } - } else if (v.isString() && v.asString().charAt(0) == '^') { + } else if (v.isString() && !v.asString().isEmpty() && v.asString().charAt(0) == '^') { String value = v.asString().substring(1).trim(); if (value.length() == 0 || value.charAt(0) == '>') @@ -526,7 +553,7 @@ else if (v.name.equals("s")) byte[] bytes = value.getBytes(("UTF-8")); md.update(bytes); byte[] digest = md.digest(); - key = Base64Coder.encodeLines(digest).substring(0, 10); + key = Base64Coder.encodeLines(digest).substring(0, InkManager.KEY_SIZE); } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { EditorLogger.error("Error encoding key." + e); return; diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/OrderedProperties.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/OrderedProperties.java index 33ab6e59f..7008c8dfe 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/OrderedProperties.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/OrderedProperties.java @@ -25,528 +25,549 @@ import java.util.Vector; /** - * This class provides an alternative to the JDK's {@link Properties} class. It fixes the design flaw of using - * inheritance over composition, while keeping up the same APIs as the original class. Keys and values are - * guaranteed to be of type {@link String}. + * This class provides an alternative to the JDK's {@link Properties} class. It + * fixes the design flaw of using inheritance over composition, while keeping up + * the same APIs as the original class. Keys and values are guaranteed to be of + * type {@link String}. *

* This class is not synchronized, contrary to the original implementation. *

- * As additional functionality, this class keeps its properties in a well-defined order. By default, the order - * is the one in which the individual properties have been added, either through explicit API calls or through + * As additional functionality, this class keeps its properties in a + * well-defined order. By default, the order is the one in which the individual + * properties have been added, either through explicit API calls or through * reading them top-to-bottom from a properties file. *

- * Also, an optional flag can be set to omit the comment that contains the current date when storing the - * properties to a properties file. + * Also, an optional flag can be set to omit the comment that contains the + * current date when storing the properties to a properties file. *

- * Currently, this class does not support the concept of default properties, contrary to the original implementation. + * Currently, this class does not support the concept of default properties, + * contrary to the original implementation. *

- * Note that this implementation is not synchronized. If multiple threads access ordered - * properties concurrently, and at least one of the threads modifies the ordered properties structurally, it - * must be synchronized externally. This is typically accomplished by synchronizing on some object - * that naturally encapsulates the properties. + * Note that this implementation is not synchronized. If + * multiple threads access ordered properties concurrently, and at least one of + * the threads modifies the ordered properties structurally, it must be + * synchronized externally. This is typically accomplished by synchronizing on + * some object that naturally encapsulates the properties. *

- * Note that the actual (and quite complex) logic of parsing and storing properties from and to a stream - * is delegated to the {@link Properties} class from the JDK. + * Note that the actual (and quite complex) logic of parsing and storing + * properties from and to a stream is delegated to the {@link Properties} class + * from the JDK. * * @see Properties */ public final class OrderedProperties implements Serializable { - private static final long serialVersionUID = 1L; - - private transient Map properties; - private transient boolean suppressDate; - - /** - * Creates a new instance that will keep the properties in the order they have been added. Other than - * the ordering of the keys, this instance behaves like an instance of the {@link Properties} class. - */ - public OrderedProperties() { - this(new LinkedHashMap(), false); - } - - private OrderedProperties(Map properties, boolean suppressDate) { - this.properties = properties; - this.suppressDate = suppressDate; - } - - /** - * See {@link Properties#getProperty(String)}. - */ - public String getProperty(String key) { - return properties.get(key); - } - - /** - * See {@link Properties#getProperty(String, String)}. - */ - public String getProperty(String key, String defaultValue) { - String value = properties.get(key); - return (value == null) ? defaultValue : value; - } - - /** - * See {@link Properties#setProperty(String, String)}. - */ - public String setProperty(String key, String value) { - return properties.put(key, value); - } - - /** - * Removes the property with the specified key, if it is present. Returns - * the value of the property, or null if there was no property with - * the specified key. - * - * @param key the key of the property to remove - * @return the previous value of the property, or null if there was no property with the specified key - */ - public String removeProperty(String key) { - return properties.remove(key); - } - - /** - * Returns true if there is a property with the specified key. - * - * @param key the key whose presence is to be tested - */ - public boolean containsProperty(String key) { - return properties.containsKey(key); - } - - /** - * See {@link Properties#size()}. - */ - public int size() { - return properties.size(); - } - - /** - * See {@link Properties#isEmpty()}. - */ - public boolean isEmpty() { - return properties.isEmpty(); - } - - /** - * See {@link Properties#propertyNames()}. - */ - public Enumeration propertyNames() { - return new Vector(properties.keySet()).elements(); - } - - /** - * See {@link Properties#stringPropertyNames()}. - */ - public Set stringPropertyNames() { - return new LinkedHashSet(properties.keySet()); - } - - /** - * See {@link Properties#entrySet()}. - */ - public Set> entrySet() { - return new LinkedHashSet>(properties.entrySet()); - } - - /** - * See {@link Properties#load(InputStream)}. - */ - public void load(InputStream stream) throws IOException { - CustomProperties customProperties = new CustomProperties(this.properties); - customProperties.load(stream); - } - - /** - * See {@link Properties#load(Reader)}. - */ - public void load(Reader reader) throws IOException { - CustomProperties customProperties = new CustomProperties(this.properties); - customProperties.load(reader); - } - - /** - * See {@link Properties#loadFromXML(InputStream)}. - */ - public void loadFromXML(InputStream stream) throws IOException, InvalidPropertiesFormatException { - CustomProperties customProperties = new CustomProperties(this.properties); - customProperties.loadFromXML(stream); - } - - /** - * See {@link Properties#store(OutputStream, String)}. - */ - public void store(OutputStream stream, String comments) throws IOException { - CustomProperties customProperties = new CustomProperties(this.properties); - if (suppressDate) { - customProperties.store(new DateSuppressingPropertiesBufferedWriter(new OutputStreamWriter(stream, "8859_1")), comments); - } else { - customProperties.store(stream, comments); - } - } - - /** - * See {@link Properties#store(Writer, String)}. - */ - public void store(Writer writer, String comments) throws IOException { - CustomProperties customProperties = new CustomProperties(this.properties); - if (suppressDate) { - customProperties.store(new DateSuppressingPropertiesBufferedWriter(writer), comments); - } else { - customProperties.store(writer, comments); - } - } - - /** - * See {@link Properties#storeToXML(OutputStream, String)}. - */ - public void storeToXML(OutputStream stream, String comment) throws IOException { - CustomProperties customProperties = new CustomProperties(this.properties); - customProperties.storeToXML(stream, comment); - } - - /** - * See {@link Properties#storeToXML(OutputStream, String, String)}. - */ - public void storeToXML(OutputStream stream, String comment, String encoding) throws IOException { - CustomProperties customProperties = new CustomProperties(this.properties); - customProperties.storeToXML(stream, comment, encoding); - } - - /** - * See {@link Properties#list(PrintStream)}. - */ - public void list(PrintStream stream) { - CustomProperties customProperties = new CustomProperties(this.properties); - customProperties.list(stream); - } - - /** - * See {@link Properties#list(PrintWriter)}. - */ - public void list(PrintWriter writer) { - CustomProperties customProperties = new CustomProperties(this.properties); - customProperties.list(writer); - } - - /** - * Convert this instance to a {@link Properties} instance. - * - * @return the {@link Properties} instance - */ - public Properties toJdkProperties() { - Properties jdkProperties = new Properties(); - for (Map.Entry entry : this.entrySet()) { - jdkProperties.put(entry.getKey(), entry.getValue()); - } - return jdkProperties; - } - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } - - if (other == null || getClass() != other.getClass()) { - return false; - } - - OrderedProperties that = (OrderedProperties) other; - return Arrays.equals(properties.entrySet().toArray(), that.properties.entrySet().toArray()); - } - - @Override - public int hashCode() { - return Arrays.hashCode(properties.entrySet().toArray()); - } - - private void writeObject(ObjectOutputStream stream) throws IOException { - stream.defaultWriteObject(); - stream.writeObject(properties); - stream.writeBoolean(suppressDate); - } - - @SuppressWarnings("unchecked") - private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { - stream.defaultReadObject(); - properties = (Map) stream.readObject(); - suppressDate = stream.readBoolean(); - } - - /** - * See {@link Properties#toString()}. - */ - @Override - public String toString() { - return properties.toString(); - } - - /** - * Creates a new instance that will have both the same property entries and - * the same behavior as the given source. - *

- * Note that the source instance and the copy instance will share the same - * comparator instance if a custom ordering had been configured on the source. - * - * @param source the source to copy from - * @return the copy - */ - public static OrderedProperties copyOf(OrderedProperties source) { - // create a copy that has the same behaviour - OrderedPropertiesBuilder builder = new OrderedPropertiesBuilder(); - builder.withSuppressDateInComment(source.suppressDate); - if (source.properties instanceof TreeMap) { - builder.withOrdering(((TreeMap) source.properties).comparator()); - } - OrderedProperties result = builder.build(); - - // copy the properties from the source to the target - for (Map.Entry entry : source.entrySet()) { - result.setProperty(entry.getKey(), entry.getValue()); - } - return result; - } - - /** - * Builder for {@link OrderedProperties} instances. - */ - public static final class OrderedPropertiesBuilder { - - private Comparator comparator; - private boolean suppressDate; - - /** - * Use a custom ordering of the keys. - * - * @param comparator the ordering to apply on the keys - * @return the builder - */ - public OrderedPropertiesBuilder withOrdering(Comparator comparator) { - this.comparator = comparator; - return this; - } - - /** - * Suppress the comment that contains the current date when storing the properties. - * - * @param suppressDate whether to suppress the comment that contains the current date - * @return the builder - */ - public OrderedPropertiesBuilder withSuppressDateInComment(boolean suppressDate) { - this.suppressDate = suppressDate; - return this; - } - - public OrderedPropertiesBuilder withOrdering() { - this.comparator = String.CASE_INSENSITIVE_ORDER; - - return this; - } - - /** - * Builds a new {@link OrderedProperties} instance. - * - * @return the new instance - */ - public OrderedProperties build() { - Map properties = (this.comparator != null) ? - new TreeMap(comparator) : - new LinkedHashMap(); - return new OrderedProperties(properties, suppressDate); - } - - } - - /** - * Custom {@link Properties} that delegates reading, writing, and enumerating properties to the - * backing {@link OrderedProperties} instance's properties. - */ - @SuppressWarnings("serial") + private static final long serialVersionUID = 1L; + + private transient Map properties; + private transient boolean suppressDate; + + /** + * Creates a new instance that will keep the properties in the order they have + * been added. Other than the ordering of the keys, this instance behaves like + * an instance of the {@link Properties} class. + */ + public OrderedProperties() { + this(new LinkedHashMap(), false); + } + + private OrderedProperties(Map properties, boolean suppressDate) { + this.properties = properties; + this.suppressDate = suppressDate; + } + + /** + * See {@link Properties#getProperty(String)}. + */ + public String getProperty(String key) { + return properties.get(key); + } + + /** + * See {@link Properties#getProperty(String, String)}. + */ + public String getProperty(String key, String defaultValue) { + String value = properties.get(key); + return (value == null) ? defaultValue : value; + } + + /** + * See {@link Properties#setProperty(String, String)}. + */ + public String setProperty(String key, String value) { + return properties.put(key, value); + } + + /** + * Removes the property with the specified key, if it is present. Returns the + * value of the property, or null if there was no property with the + * specified key. + * + * @param key the key of the property to remove + * @return the previous value of the property, or null if there was no + * property with the specified key + */ + public String removeProperty(String key) { + return properties.remove(key); + } + + /** + * Returns true if there is a property with the specified key. + * + * @param key the key whose presence is to be tested + */ + public boolean containsProperty(String key) { + return properties.containsKey(key); + } + + /** + * See {@link Properties#size()}. + */ + public int size() { + return properties.size(); + } + + /** + * See {@link Properties#isEmpty()}. + */ + public boolean isEmpty() { + return properties.isEmpty(); + } + + /** + * See {@link Properties#propertyNames()}. + */ + public Enumeration propertyNames() { + return new Vector<>(properties.keySet()).elements(); + } + + /** + * See {@link Properties#stringPropertyNames()}. + */ + public Set stringPropertyNames() { + return new LinkedHashSet<>(properties.keySet()); + } + + /** + * See {@link Properties#entrySet()}. + */ + public Set> entrySet() { + return new LinkedHashSet<>(properties.entrySet()); + } + + /** + * See {@link Properties#load(InputStream)}. + */ + public void load(InputStream stream) throws IOException { + CustomProperties customProperties = new CustomProperties(this.properties); + customProperties.load(stream); + } + + /** + * See {@link Properties#load(Reader)}. + */ + public void load(Reader reader) throws IOException { + CustomProperties customProperties = new CustomProperties(this.properties); + customProperties.load(reader); + } + + /** + * See {@link Properties#loadFromXML(InputStream)}. + */ + public void loadFromXML(InputStream stream) throws IOException, InvalidPropertiesFormatException { + CustomProperties customProperties = new CustomProperties(this.properties); + customProperties.loadFromXML(stream); + } + + /** + * See {@link Properties#store(OutputStream, String)}. + */ + public void store(OutputStream stream, String comments) throws IOException { + CustomProperties customProperties = new CustomProperties(this.properties); + if (suppressDate) { + customProperties.store( + new DateSuppressingPropertiesBufferedWriter(new OutputStreamWriter(stream, "8859_1")), comments); + } else { + customProperties.store(stream, comments); + } + } + + /** + * See {@link Properties#store(Writer, String)}. + */ + public void store(Writer writer, String comments) throws IOException { + CustomProperties customProperties = new CustomProperties(this.properties); + if (suppressDate) { + customProperties.store(new DateSuppressingPropertiesBufferedWriter(writer), comments); + } else { + customProperties.store(writer, comments); + } + } + + /** + * See {@link Properties#storeToXML(OutputStream, String)}. + */ + public void storeToXML(OutputStream stream, String comment) throws IOException { + CustomProperties customProperties = new CustomProperties(this.properties); + customProperties.storeToXML(stream, comment); + } + + /** + * See {@link Properties#storeToXML(OutputStream, String, String)}. + */ + public void storeToXML(OutputStream stream, String comment, String encoding) throws IOException { + CustomProperties customProperties = new CustomProperties(this.properties); + customProperties.storeToXML(stream, comment, encoding); + } + + /** + * See {@link Properties#list(PrintStream)}. + */ + public void list(PrintStream stream) { + CustomProperties customProperties = new CustomProperties(this.properties); + customProperties.list(stream); + } + + /** + * See {@link Properties#list(PrintWriter)}. + */ + public void list(PrintWriter writer) { + CustomProperties customProperties = new CustomProperties(this.properties); + customProperties.list(writer); + } + + /** + * Convert this instance to a {@link Properties} instance. + * + * @return the {@link Properties} instance + */ + public Properties toJdkProperties() { + Properties jdkProperties = new Properties(); + for (Map.Entry entry : this.entrySet()) { + jdkProperties.put(entry.getKey(), entry.getValue()); + } + return jdkProperties; + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if (other == null || getClass() != other.getClass()) { + return false; + } + + OrderedProperties that = (OrderedProperties) other; + return Arrays.equals(properties.entrySet().toArray(), that.properties.entrySet().toArray()); + } + + @Override + public int hashCode() { + return Arrays.hashCode(properties.entrySet().toArray()); + } + + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(properties); + stream.writeBoolean(suppressDate); + } + + @SuppressWarnings("unchecked") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + properties = (Map) stream.readObject(); + suppressDate = stream.readBoolean(); + } + + /** + * See {@link Properties#toString()}. + */ + @Override + public String toString() { + return properties.toString(); + } + + /** + * Creates a new instance that will have both the same property entries and the + * same behavior as the given source. + *

+ * Note that the source instance and the copy instance will share the same + * comparator instance if a custom ordering had been configured on the source. + * + * @param source the source to copy from + * @return the copy + */ + public static OrderedProperties copyOf(OrderedProperties source) { + // create a copy that has the same behaviour + OrderedPropertiesBuilder builder = new OrderedPropertiesBuilder(); + builder.withSuppressDateInComment(source.suppressDate); + if (source.properties instanceof TreeMap) { + builder.withOrdering(((TreeMap) source.properties).comparator()); + } + OrderedProperties result = builder.build(); + + // copy the properties from the source to the target + for (Map.Entry entry : source.entrySet()) { + result.setProperty(entry.getKey(), entry.getValue()); + } + return result; + } + + /** + * Builder for {@link OrderedProperties} instances. + */ + public static final class OrderedPropertiesBuilder { + + private Comparator comparator; + private boolean suppressDate; + + /** + * Use a custom ordering of the keys. + * + * @param comparator the ordering to apply on the keys + * @return the builder + */ + public OrderedPropertiesBuilder withOrdering(Comparator comparator) { + this.comparator = comparator; + return this; + } + + /** + * Suppress the comment that contains the current date when storing the + * properties. + * + * @param suppressDate whether to suppress the comment that contains the current + * date + * @return the builder + */ + public OrderedPropertiesBuilder withSuppressDateInComment(boolean suppressDate) { + this.suppressDate = suppressDate; + return this; + } + + public OrderedPropertiesBuilder withOrderingCaseSensitive() { + this.comparator = new Comparator() { + + @Override + public int compare(String a, String b) { + return a.compareTo(b); + } + }; + + return this; + + } + + public OrderedPropertiesBuilder withOrdering() { + this.comparator = String.CASE_INSENSITIVE_ORDER; + + return this; + } + + /** + * Builds a new {@link OrderedProperties} instance. + * + * @return the new instance + */ + public OrderedProperties build() { + Map properties = (this.comparator != null) ? new TreeMap(comparator) + : new LinkedHashMap(); + return new OrderedProperties(properties, suppressDate); + } + + } + + /** + * Custom {@link Properties} that delegates reading, writing, and enumerating + * properties to the backing {@link OrderedProperties} instance's properties. + */ + @SuppressWarnings("serial") private static final class CustomProperties extends Properties { - private final Map targetProperties; - - private CustomProperties(Map targetProperties) { - this.targetProperties = targetProperties; - } - - @Override - public Object get(Object key) { - return targetProperties.get(key); - } - - @Override - public Object put(Object key, Object value) { - return targetProperties.put((String) key, (String) value); - } - - @Override - public String getProperty(String key) { - return targetProperties.get(key); - } - - @Override - public Enumeration keys() { - return new Vector(targetProperties.keySet()).elements(); - } - - @Override - public Set keySet() { - return new LinkedHashSet(targetProperties.keySet()); - } - - @Override - public void store(Writer writer, String comments) throws IOException { - store1((writer instanceof BufferedWriter) ? (BufferedWriter) writer : new BufferedWriter(writer), comments, - false); - } - - @Override - public void store(OutputStream out, String comments) throws IOException { - store1(new BufferedWriter(new OutputStreamWriter(out, "8859_1")), comments, true); - } - - private void store1(BufferedWriter bw, String comments, boolean escUnicode) throws IOException { - - synchronized (this) { - for (Enumeration e = keys(); e.hasMoreElements();) { - String key = (String) e.nextElement(); - String val = (String) get(key); - key = saveConvert(key, true, escUnicode); - /* - * No need to escape embedded and trailing spaces for value, - * hence pass false to flag. - */ - val = saveConvert(val, false, escUnicode); - bw.write(key + "=" + val); - bw.newLine(); - } - } - bw.flush(); - } - - /* - * Converts unicodes to encoded \uxxxx and escapes special characters - * with a preceding slash - */ - private String saveConvert(String theString, boolean escapeSpace, boolean escapeUnicode) { - int len = theString.length(); - int bufLen = len * 2; - if (bufLen < 0) { - bufLen = Integer.MAX_VALUE; - } - StringBuilder outBuffer = new StringBuilder(bufLen); - - for (int x = 0; x < len; x++) { - char aChar = theString.charAt(x); - // Handle common case first, selecting largest block that - // avoids the specials below - if ((aChar > 61) && (aChar < 127)) { - if (aChar == '\\') { - outBuffer.append('\\'); - outBuffer.append('\\'); - continue; - } - outBuffer.append(aChar); - continue; - } - switch (aChar) { - case ' ': - if (x == 0 || escapeSpace) - outBuffer.append('\\'); - outBuffer.append(' '); - break; - case '\t': - outBuffer.append('\\'); - outBuffer.append('t'); - break; - case '\n': - outBuffer.append('\\'); - outBuffer.append('n'); - break; - case '\r': - outBuffer.append('\\'); - outBuffer.append('r'); - break; - case '\f': - outBuffer.append('\\'); - outBuffer.append('f'); - break; - case '=': // Fall through - case ':': // Fall through - case '#': // Fall through + private final Map targetProperties; + + private CustomProperties(Map targetProperties) { + this.targetProperties = targetProperties; + } + + @Override + public Object get(Object key) { + return targetProperties.get(key); + } + + @Override + public Object put(Object key, Object value) { + return targetProperties.put((String) key, (String) value); + } + + @Override + public String getProperty(String key) { + return targetProperties.get(key); + } + + @Override + public Enumeration keys() { + return new Vector(targetProperties.keySet()).elements(); + } + + @Override + public Set keySet() { + return new LinkedHashSet(targetProperties.keySet()); + } + + @Override + public void store(Writer writer, String comments) throws IOException { + store1((writer instanceof BufferedWriter) ? (BufferedWriter) writer : new BufferedWriter(writer), comments, + false); + } + + @Override + public void store(OutputStream out, String comments) throws IOException { + store1(new BufferedWriter(new OutputStreamWriter(out, "8859_1")), comments, true); + } + + private void store1(BufferedWriter bw, String comments, boolean escUnicode) throws IOException { + + synchronized (this) { + for (Enumeration e = keys(); e.hasMoreElements();) { + String key = (String) e.nextElement(); + String val = (String) get(key); + key = saveConvert(key, true, escUnicode); + /* + * No need to escape embedded and trailing spaces for value, hence pass false to + * flag. + */ + val = saveConvert(val, false, escUnicode); + bw.write(key + "=" + val); + bw.newLine(); + } + } + bw.flush(); + } + + /* + * Converts unicodes to encoded \uxxxx and escapes special characters with a + * preceding slash + */ + private String saveConvert(String theString, boolean escapeSpace, boolean escapeUnicode) { + int len = theString.length(); + int bufLen = len * 2; + if (bufLen < 0) { + bufLen = Integer.MAX_VALUE; + } + StringBuilder outBuffer = new StringBuilder(bufLen); + + for (int x = 0; x < len; x++) { + char aChar = theString.charAt(x); + // Handle common case first, selecting largest block that + // avoids the specials below + if ((aChar > 61) && (aChar < 127)) { + if (aChar == '\\') { + outBuffer.append('\\'); + outBuffer.append('\\'); + continue; + } + outBuffer.append(aChar); + continue; + } + switch (aChar) { + case ' ': + if (x == 0 || escapeSpace) + outBuffer.append('\\'); + outBuffer.append(' '); + break; + case '\t': + outBuffer.append('\\'); + outBuffer.append('t'); + break; + case '\n': + outBuffer.append('\\'); + outBuffer.append('n'); + break; + case '\r': + outBuffer.append('\\'); + outBuffer.append('r'); + break; + case '\f': + outBuffer.append('\\'); + outBuffer.append('f'); + break; + case '=': // Fall through + case ':': // Fall through + case '#': // Fall through // case '!': - outBuffer.append('\\'); - outBuffer.append(aChar); - break; - default: - if (((aChar < 0x0020) || (aChar > 0x007e)) && escapeUnicode) { - outBuffer.append('\\'); - outBuffer.append('u'); - outBuffer.append(toHex((aChar >> 12) & 0xF)); - outBuffer.append(toHex((aChar >> 8) & 0xF)); - outBuffer.append(toHex((aChar >> 4) & 0xF)); - outBuffer.append(toHex(aChar & 0xF)); - } else { - outBuffer.append(aChar); - } - } - } - return outBuffer.toString(); - } - - /** - * Convert a nibble to a hex character - * - * @param nibble - * the nibble to convert. - */ - private static char toHex(int nibble) { - return hexDigit[(nibble & 0xF)]; - } - - private static final char[] hexDigit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', - 'F' }; - - } - - /** - * Custom {@link BufferedWriter} for storing properties that will write all leading lines of comments except - * the last comment line. Using the JDK Properties class to store properties, the last comment - * line always contains the current date which is what we want to filter out. - */ - private static final class DateSuppressingPropertiesBufferedWriter extends BufferedWriter { - - private final String LINE_SEPARATOR = System.getProperty("line.separator"); - - private StringBuilder currentComment; - private String previousComment; - - private DateSuppressingPropertiesBufferedWriter(Writer out) { - super(out); - } - - @Override - public void write(String string) throws IOException { - if (currentComment != null) { - currentComment.append(string); - if (string.endsWith(LINE_SEPARATOR)) { - if (previousComment != null) { - super.write(previousComment); - } - - previousComment = currentComment.toString(); - currentComment = null; - } - } else if (string.startsWith("#")) { - currentComment = new StringBuilder(string); - } else { - super.write(string); - } - } - - } + outBuffer.append('\\'); + outBuffer.append(aChar); + break; + default: + if (((aChar < 0x0020) || (aChar > 0x007e)) && escapeUnicode) { + outBuffer.append('\\'); + outBuffer.append('u'); + outBuffer.append(toHex((aChar >> 12) & 0xF)); + outBuffer.append(toHex((aChar >> 8) & 0xF)); + outBuffer.append(toHex((aChar >> 4) & 0xF)); + outBuffer.append(toHex(aChar & 0xF)); + } else { + outBuffer.append(aChar); + } + } + } + return outBuffer.toString(); + } + + /** + * Convert a nibble to a hex character + * + * @param nibble the nibble to convert. + */ + private static char toHex(int nibble) { + return hexDigit[(nibble & 0xF)]; + } + + private static final char[] hexDigit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', + 'E', 'F' }; + + } + + /** + * Custom {@link BufferedWriter} for storing properties that will write all + * leading lines of comments except the last comment line. Using the JDK + * Properties class to store properties, the last comment line always contains + * the current date which is what we want to filter out. + */ + private static final class DateSuppressingPropertiesBufferedWriter extends BufferedWriter { + + private final String LINE_SEPARATOR = System.getProperty("line.separator"); + + private StringBuilder currentComment; + private String previousComment; + + private DateSuppressingPropertiesBufferedWriter(Writer out) { + super(out); + } + + @Override + public void write(String string) throws IOException { + if (currentComment != null) { + currentComment.append(string); + if (string.endsWith(LINE_SEPARATOR)) { + if (previousComment != null) { + super.write(previousComment); + } + + previousComment = currentComment.toString(); + currentComment = null; + } + } else if (string.startsWith("#")) { + currentComment = new StringBuilder(string); + } else { + super.write(string); + } + } + + } } - diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/RunProccess.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/RunProccess.java index 526d3dabd..42f285160 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/RunProccess.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/RunProccess.java @@ -168,6 +168,24 @@ public static Process runJavaProccess(String mainClass, List classpathEn return processBuilder.start(); } + public static Process runJavaHomeBin(String bin, List args) throws IOException { + String cmd = System.getProperty("java.home") + "/bin/" + bin; + String workingDirectory = "."; + + List argumentsList = new ArrayList<>(); + argumentsList.add(cmd); + + if (args != null) + argumentsList.addAll(args); + + ProcessBuilder processBuilder = new ProcessBuilder(argumentsList.toArray(new String[argumentsList.size()])); + // processBuilder.redirectErrorStream(true); + processBuilder.directory(new File(workingDirectory)); + processBuilder.inheritIO(); + + return processBuilder.start(); + } + public static boolean runGradle(File workingDir, List parameters) { String exec = workingDir.getAbsolutePath() + "/" + (System.getProperty("os.name").contains("Windows") ? "gradlew.bat" : "gradlew"); diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/Versions.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/Versions.java index c26a05c28..c9e567f94 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/Versions.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/Versions.java @@ -19,23 +19,22 @@ import java.util.Properties; public class Versions { - private static final String VERSION_PROP="version"; - private static final String LIBGDX_VERSION_PROP="libgdxVersion"; - private static final String ROBOVM_VERSION_PROP="roboVMVersion"; - private static final String BUILDTOOLS_VERSION_PROP="buildToolsVersion"; - private static final String ANDROID_API_LEVEL_PROP="androidAPILevel"; + private static final String VERSION_PROP = "version"; + private static final String LIBGDX_VERSION_PROP = "libgdxVersion"; + private static final String ROBOVM_VERSION_PROP = "roboVMVersion"; + private static final String ANDROID_API_LEVEL_PROP = "androidAPILevel"; private static final String ANDROID_GRADLE_VERSION_PROP = "androidGradlePluginVersion"; private static final String ROBOVM_GRADLE_VERSION_PROP = "roboVMGradlePluginVersion"; private static final String BLADE_INK_VERSION_PROP = "bladeInkVersion"; - + public static final String PROPERTIES_FILENAME = "/versions.properties"; private static Properties config = null; - + private static String getProperty(String key, String defaultValue) { - if(config == null) { + if (config == null) { config = new Properties(); - + try { InputStream is = Versions.class.getResourceAsStream(PROPERTIES_FILENAME); config.load(is); @@ -43,48 +42,40 @@ private static String getProperty(String key, String defaultValue) { EditorLogger.error("ERROR LOADING VERSION FILE: " + e.getMessage()); } } - + return config.getProperty(key, defaultValue); } - + public static String getVersion() { - return getProperty(VERSION_PROP,null); + return getProperty(VERSION_PROP, null); } - + public static String getLibgdxVersion() { - return getProperty(LIBGDX_VERSION_PROP,null); + return getProperty(LIBGDX_VERSION_PROP, null); } - + public static String getRoboVMVersion() { - return getProperty(ROBOVM_VERSION_PROP,null); + return getProperty(ROBOVM_VERSION_PROP, null); } - - public static String getBuildToolsVersion() { - return getProperty(BUILDTOOLS_VERSION_PROP,null); - } - + public static String getAndroidAPILevel() { - return getProperty(ANDROID_API_LEVEL_PROP,null); + return getProperty(ANDROID_API_LEVEL_PROP, null); } - - public static void setBuildToolsVersion(String v) { - config.setProperty(BUILDTOOLS_VERSION_PROP, v); - } - + public static void setAndroidAPILevel(String v) { config.setProperty(ANDROID_API_LEVEL_PROP, v); } - + public static String getAndroidGradlePluginVersion() { - return getProperty(ANDROID_GRADLE_VERSION_PROP,null); + return getProperty(ANDROID_GRADLE_VERSION_PROP, null); } - + public static String getROBOVMGradlePluginVersion() { - return getProperty(ROBOVM_GRADLE_VERSION_PROP,null); + return getProperty(ROBOVM_GRADLE_VERSION_PROP, null); } - + public static String getBladeInkVersion() { - return getProperty(BLADE_INK_VERSION_PROP,null); + return getProperty(BLADE_INK_VERSION_PROP, null); } } diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/model/I18NHandler.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/model/I18NHandler.java index 2c83f0797..0313bd8e6 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/model/I18NHandler.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/model/I18NHandler.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; +import java.util.Map; import com.bladecoder.engine.actions.Action; import com.bladecoder.engine.actions.DisableActionAction; @@ -195,7 +196,7 @@ public void putTranslationsInElement(BaseActor a) { if (a instanceof SpriteActor && ((SpriteActor) a).getRenderer() instanceof TextRenderer) { TextRenderer r = (TextRenderer) ((SpriteActor) a).getRenderer(); - + r.setText(getTranslation(r.getText())); } } @@ -377,33 +378,58 @@ public String genKey(String sceneid, String actorid, String parent, int pos, Str } public String getNotDuplicateKey(String key) { - // first delete all '_' at the end to avoid always growing keys - int idx = key.indexOf('_'); - - if(idx != -1) - key = key.substring(0, idx); - - while (i18nChapter.containsProperty(key.charAt(0) == I18N.PREFIX ? key.substring(1) : key)) - key += '_'; - - return key; + // first delete all numbers at the end to avoid always growing keys + String key2 = key.replaceAll("\\d*$", ""); + + if (key2.isEmpty()) + key2 = key; + + int i = 0; + + while (i18nChapter.containsProperty(key2.charAt(0) == I18N.PREFIX ? key2.substring(1) : key2)) { + i++; + key2 = key + i; + } + + return key2; } public String getNotDuplicateKeyWorld(String key) { - // first delete all '_' at the end - int idx = key.indexOf('_'); - - if(idx != -1) - key = key.substring(0, idx); - - while (i18nWorld.containsProperty(key.charAt(0) == I18N.PREFIX ? key.substring(1) : key)) - key += '_'; - - return key; + // first delete all numbers at the end to avoid always growing keys + String key2 = key.replaceAll("\\d*$", ""); + + if (key2.isEmpty()) + key2 = key; + + int i = 0; + + while (i18nWorld.containsProperty(key2.charAt(0) == I18N.PREFIX ? key2.substring(1) : key2)) { + i++; + key2 = key + i; + } + + return key2; + } + + public void cleanI18N() { + Map scenes = Ctx.project.getWorld().getScenes(); + for (Scene scn : scenes.values()) { + Ctx.project.getI18N().putTranslationsInElement(scn); + } + + // i18nChapter = new + // OrderedPropertiesBuilder().withSuppressDateInComment(true).withOrdering().build(); + deleteUnusedKeys(); + + for (Scene scn : scenes.values()) { + Ctx.project.getI18N().extractStrings(scn); + } + + Ctx.project.setModified(); } private void deleteUnusedKeys() { - ArrayList usedKeys = new ArrayList(); + ArrayList usedKeys = new ArrayList<>(); // SCENES for (Scene s : Ctx.project.getWorld().getScenes().values()) @@ -429,7 +455,7 @@ private void deleteUnusedKeys() { keys = i18nWorld.propertyNames(); while (keys.hasMoreElements()) { - String key = (String) keys.nextElement(); + String key = keys.nextElement(); // Doesn't remove ui keys if (!usedKeys.contains(key) && !key.startsWith("ui.")) { diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/model/Project.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/model/Project.java index 6e2fe7f2e..219be1065 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/model/Project.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/model/Project.java @@ -41,9 +41,9 @@ import com.bladecoder.engineeditor.common.EditorLogger; import com.bladecoder.engineeditor.common.FolderClassLoader; import com.bladecoder.engineeditor.common.OrderedProperties; +import com.bladecoder.engineeditor.common.OrderedProperties.OrderedPropertiesBuilder; import com.bladecoder.engineeditor.common.RunProccess; import com.bladecoder.engineeditor.common.Versions; -import com.bladecoder.engineeditor.common.OrderedProperties.OrderedPropertiesBuilder; import com.bladecoder.engineeditor.setup.BladeEngineSetup; import com.bladecoder.engineeditor.undo.UndoStack; @@ -72,8 +72,7 @@ public class Project extends PropertyChange { public static final String PARTICLE_RENDERER_STRING = "particle"; public static final String TEXT_RENDERER_STRING = "text"; - public static final String NEW_ASSETS_PATH = "/assets"; - public static final String OLD_ASSETS_PATH = "/android/assets"; + public static final String ASSETS_PATH = "/assets"; public static final String MODEL_PATH = "/model"; public static final String ATLASES_PATH = "/atlases"; public static final String FONTS_PATH = "/fonts"; @@ -113,19 +112,13 @@ public class Project extends PropertyChange { public Project() { loadConfig(); } - + public World getWorld() { return world; } public String getAssetPath(String base) { - String path = base + NEW_ASSETS_PATH; - - if (new File(path).exists()) { - return path; - } else { - return base + OLD_ASSETS_PATH; - } + return base + ASSETS_PATH; } public String getAssetPath() { @@ -254,7 +247,7 @@ public String getTitle() { return projectConfig.getProperty(Config.TITLE_PROP, getProjectDir().getName()); } - + public boolean isLoaded() { return Ctx.project.getProjectDir() != null; } @@ -302,14 +295,14 @@ public void saveProject() throws IOException { // 3.- SAVE BladeEngine.properties List resolutions = getResolutions(); StringBuilder sb = new StringBuilder(); - - for(int i = 0; i < resolutions.size(); i++) { + + for (int i = 0; i < resolutions.size(); i++) { sb.append(resolutions.get(i)); - - if(i < resolutions.size() - 1) + + if (i < resolutions.size() - 1) sb.append(','); } - + projectConfig.setProperty(Config.RESOLUTIONS, sb.toString()); projectConfig.store(new FileOutputStream(getAssetPath() + "/" + Config.PROPERTIES_FILENAME), null); @@ -341,9 +334,10 @@ public void loadProject(File projectToLoad) throws IOException { // Use FolderClassLoader for loading CUSTOM actions. // TODO Add 'core/bin' and '/core/out' folders??? FolderClassLoader folderClassLoader = null; - + if (new File(projectFile, "/assets").exists()) { - folderClassLoader = new FolderClassLoader(projectFile.getAbsolutePath() + "/core/build/classes/java/main"); + folderClassLoader = new FolderClassLoader( + projectFile.getAbsolutePath() + "/core/build/classes/java/main"); } else { folderClassLoader = new FolderClassLoader(projectFile.getAbsolutePath() + "/core/build/classes/main"); } @@ -352,6 +346,10 @@ public void loadProject(File projectToLoad) throws IOException { EngineAssetManager.createEditInstance(getAssetPath()); try { + // Clear last project to avoid reloading if the project fails. + getEditorConfig().remove(LAST_PROJECT_PROP); + saveConfig(); + world.loadWorldDesc(); } catch (SerializationException ex) { // check for not compiled custom actions @@ -376,9 +374,8 @@ public void loadProject(File projectToLoad) throws IOException { // No need to load the chapter. It's loaded by the chapter combo. // loadChapter(world.getInitChapter()); - editorConfig.setProperty(LAST_PROJECT_PROP, projectFile.getAbsolutePath()); - - projectConfig = new OrderedPropertiesBuilder().withSuppressDateInComment(true).withOrdering().build(); + projectConfig = new OrderedPropertiesBuilder().withSuppressDateInComment(true).withOrderingCaseSensitive() + .build(); projectConfig.load(new FileInputStream(getAssetPath() + "/" + Config.PROPERTIES_FILENAME)); modified = false; @@ -395,8 +392,7 @@ public boolean checkVersion(File projectPath) throws FileNotFoundException, IOEx String editorVersion = getEditorBladeEngineVersion(); String projectVersion = getProjectBladeEngineVersion(projectPath); - if (editorVersion.equals(projectVersion) - || editorVersion.indexOf('.') == -1) + if (editorVersion.equals(projectVersion) || editorVersion.indexOf('.') == -1) return true; if (parseVersion(editorVersion) <= parseVersion(projectVersion)) @@ -407,12 +403,12 @@ public boolean checkVersion(File projectPath) throws FileNotFoundException, IOEx private int parseVersion(String v) { int number = 1; // 1 -> release, 0 -> snapshot - - if(v.endsWith("-SNAPSHOT")) { + + if (v.endsWith("-SNAPSHOT")) { number = 0; v = v.substring(0, v.length() - "-SNAPSHOT".length()); } - + String[] split = v.split("\\."); try { @@ -473,7 +469,7 @@ public BaseActor getActor(String id) { public List getResolutions() { File atlasesPath = new File(getAssetPath() + UI_PATH); - ArrayList l = new ArrayList(); + ArrayList l = new ArrayList<>(); File[] list = atlasesPath.listFiles(); @@ -509,13 +505,14 @@ public void loadChapter(String selChapter) throws IOException { try { chapter.load(selChapter); firePropertyChange(NOTIFY_CHAPTER_LOADED); + getEditorConfig().setProperty(LAST_PROJECT_PROP, projectFile.getAbsolutePath()); getEditorConfig().setProperty("project.selectedChapter", selChapter); } catch (SerializationException ex) { // check for not compiled custom actions if (ex.getCause() != null && ex.getCause() instanceof ClassNotFoundException) { EditorLogger.msg("Custom action class not found. Trying to compile..."); if (RunProccess.runGradle(getProjectDir(), "desktop:compileJava")) { - ((FolderClassLoader)ActionFactory.getActionClassLoader()).reload(); + ((FolderClassLoader) ActionFactory.getActionClassLoader()).reload(); chapter.load(selChapter); } else { throw new IOException("Failed to run Gradle."); @@ -538,7 +535,8 @@ public void setModified() { } public OrderedProperties getGradleProperties(File projectPath) throws FileNotFoundException, IOException { - OrderedProperties prop = new OrderedPropertiesBuilder().withSuppressDateInComment(true).withOrdering().build(); + OrderedProperties prop = new OrderedPropertiesBuilder().withSuppressDateInComment(true) + .withOrderingCaseSensitive().build(); prop.load(new FileReader(projectPath.getAbsolutePath() + "/gradle.properties")); @@ -546,8 +544,7 @@ public OrderedProperties getGradleProperties(File projectPath) throws FileNotFou } public void saveGradleProperties(OrderedProperties prop, File projectPath) throws IOException { - FileOutputStream os = new FileOutputStream( - projectPath.getAbsolutePath() + "/gradle.properties"); + FileOutputStream os = new FileOutputStream(projectPath.getAbsolutePath() + "/gradle.properties"); prop.store(os, null); } diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/scneditor/ScnWidgetInputListener.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/scneditor/ScnWidgetInputListener.java index 2bc358703..eda9efdea 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/scneditor/ScnWidgetInputListener.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/scneditor/ScnWidgetInputListener.java @@ -47,7 +47,8 @@ public class ScnWidgetInputListener extends ClickListener { private final ScnWidget scnWidget; public static enum DraggingModes { - NONE, DRAGGING_ACTOR, DRAGGING_BBOX_POINT, DRAGGING_MARKER_0, DRAGGING_MARKER_100, DRAGGING_REFPOINT, ROTATE_ACTOR, SCALE_LOCK_ACTOR, SCALE_ACTOR + NONE, DRAGGING_ACTOR, DRAGGING_BBOX_POINT, DRAGGING_MARKER_0, DRAGGING_MARKER_100, DRAGGING_REFPOINT, + ROTATE_ACTOR, SCALE_LOCK_ACTOR, SCALE_ACTOR }; private DraggingModes draggingMode = DraggingModes.NONE; @@ -159,11 +160,11 @@ public boolean touchDown(InputEvent event, float x, float y, int pointer, int bu undoOrg.set(selActor.getX(), selActor.getY()); return true; } - - if(selActor instanceof AnchorActor) { + + if (selActor instanceof AnchorActor) { float orgX = selActor.getX(); float orgY = selActor.getY(); - + float dst = Vector2.dst(p.x, p.y, orgX, orgY); if (dst < Scene.ANCHOR_RADIUS) { @@ -338,11 +339,11 @@ private void updateFakeDepth() { } @Override - public boolean scrolled(InputEvent event, float x, float y, int amount) { - super.scrolled(event, x, y, amount); + public boolean scrolled(InputEvent event, float x, float y, float amountX, float amountY) { + super.scrolled(event, x, y, amountX, amountY); // EditorLogger.debug("SCROLLED - X: " + x + " Y: " + y); - scnWidget.zoom(amount); + scnWidget.zoom((int) amountY); return true; } @@ -459,6 +460,7 @@ public void enter(InputEvent event, float x, float y, int pointer, super.enter(event, x, y, pointer, fromActor); // EditorLogger.debug("ENTER - X: " + x + " Y: " + y); scnWidget.getStage().setScrollFocus(scnWidget); + scnWidget.getStage().setKeyboardFocus(scnWidget); } } diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/scneditor/ToolsWindow.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/scneditor/ToolsWindow.java index 2c494bd2a..d51c03beb 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/scneditor/ToolsWindow.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/scneditor/ToolsWindow.java @@ -228,13 +228,6 @@ protected void createUIAtlas() { @Override public void selected(Array files) { - // fileChooser.setTitle("Select the file to export the project - // texts"); - - // ImageUtils.createAtlas(files.get(0).file().getAbsolutePath(), - // Ctx.project.getProjectPath() + "/" + Project.UI_PATH + "/1/", - // "ui", 1, TextureFilter.Linear, - // TextureFilter.Nearest); List res = Ctx.project.getResolutions(); @@ -246,7 +239,7 @@ public void selected(Array files) { ImageUtils.createAtlas(files.get(0).file().getAbsolutePath(), Ctx.project.getAssetPath() + Project.UI_PATH + "/" + r, "ui" + ".atlas", scale, maxWH, - maxWH, TextureFilter.Linear, TextureFilter.Nearest, "png"); + maxWH, TextureFilter.Linear, TextureFilter.Nearest, "png", false); } catch (IOException e) { EditorLogger.error(e.getMessage()); Message.showMsgDialog(getStage(), "Error creating atlas", e.getMessage()); diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/setup/BladeEngineSetup.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/setup/BladeEngineSetup.java index ae6a59915..d8fb80dc4 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/setup/BladeEngineSetup.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/setup/BladeEngineSetup.java @@ -37,13 +37,11 @@ /** * Command line tool to generate libgdx projects * - * @author badlogic - * @author Tomski * @author rgarcia */ public class BladeEngineSetup { public static boolean isSdkLocationValid(String sdkLocation) { - return new File(sdkLocation, "tools").exists() && new File(sdkLocation, "platforms").exists(); + return new File(sdkLocation, "platforms").exists(); } public static boolean isEmptyDirectory(String destination) { @@ -55,12 +53,6 @@ public static boolean isEmptyDirectory(String destination) { } public static boolean isSdkUpToDate(String sdkLocation) { - File buildTools = new File(sdkLocation, "build-tools"); - if (!buildTools.exists()) { - EditorLogger.error("You have no build tools!\nUpdate your Android SDK with build tools version: " - + Versions.getBuildToolsVersion()); - return false; - } File apis = new File(sdkLocation, "platforms"); if (!apis.exists()) { @@ -68,24 +60,9 @@ public static boolean isSdkUpToDate(String sdkLocation) { + Versions.getAndroidAPILevel()); return false; } - String newestLocalTool = getLatestTools(buildTools); - int[] localToolVersion = convertTools(newestLocalTool); - int[] targetToolVersion = convertTools(Versions.getBuildToolsVersion()); - if (compareVersions(targetToolVersion, localToolVersion)) { - // ALWAYS USE THE CURRENT BUILD TOOLS - Versions.setBuildToolsVersion(newestLocalTool); - - EditorLogger.error("Using build tools: " + Versions.getBuildToolsVersion()); - } else { - if (!hasFileInDirectory(buildTools, Versions.getBuildToolsVersion())) { - EditorLogger.error( - "Please update your Android SDK, you need build tools: " + Versions.getBuildToolsVersion()); - return false; - } - } int newestLocalApi = getLatestApi(apis); - if (newestLocalApi > Integer.valueOf(Versions.getAndroidAPILevel())) { + if (newestLocalApi > Integer.parseInt(Versions.getAndroidAPILevel())) { // ALWAYS USE THE CURRENT API Versions.setAndroidAPILevel(Integer.toString(newestLocalApi)); @@ -119,28 +96,6 @@ private static int getLatestApi(File apis) { return apiLevel; } - private static String getLatestTools(File buildTools) { - String version = null; - int[] versionSplit = new int[3]; - int[] testSplit = new int[3]; - for (File toolsVersion : buildTools.listFiles()) { - if (version == null) { - version = readBuildToolsVersion(toolsVersion); - versionSplit = convertTools(version); - continue; - } - testSplit = convertTools(readBuildToolsVersion(toolsVersion)); - if (compareVersions(versionSplit, testSplit)) { - version = readBuildToolsVersion(toolsVersion); - } - } - if (version != null) { - return version; - } else { - return "0.0.0"; - } - } - private static int readAPIVersion(File parentFile) { File properties = new File(parentFile, "source.properties"); FileReader reader; @@ -170,67 +125,6 @@ private static int readAPIVersion(File parentFile) { return 0; } - private static String readBuildToolsVersion(File parentFile) { - File properties = new File(parentFile, "source.properties"); - FileReader reader; - BufferedReader buffer; - try { - reader = new FileReader(properties); - buffer = new BufferedReader(reader); - - String line = null; - - while ((line = buffer.readLine()) != null) { - if (line.contains("Pkg.Revision")) { - - String versionString = line.split("\\=")[1]; - int count = versionString.split("\\.").length; - for (int i = 0; i < 3 - count; i++) { - versionString += ".0"; - } - - buffer.close(); - reader.close(); - - return versionString; - } - } - } catch (IOException e) { - EditorLogger.printStackTrace(e); - } - return "0.0.0"; - } - - private static boolean compareVersions(int[] version, int[] testVersion) { - if (testVersion[0] > version[0]) { - return true; - } else if (testVersion[0] == version[0]) { - if (testVersion[1] > version[1]) { - return true; - } else if (testVersion[1] == version[1]) { - return testVersion[2] > version[2]; - } - } - return false; - } - - private static int[] convertTools(String toolsVersion) { - String[] stringSplit = toolsVersion.split("\\."); - int[] versionSplit = new int[3]; - if (stringSplit.length == 3) { - try { - versionSplit[0] = Integer.parseInt(stringSplit[0]); - versionSplit[1] = Integer.parseInt(stringSplit[1]); - versionSplit[2] = Integer.parseInt(stringSplit[2]); - return versionSplit; - } catch (NumberFormatException nfe) { - return new int[] { 0, 0, 0 }; - } - } else { - return new int[] { 0, 0, 0 }; - } - } - public void build(String outputDir, String appName, String packageName, String mainClass, String sdkLocation, boolean spinePlugin) throws IOException { ProjectSetup project = new ProjectSetup(); @@ -264,11 +158,14 @@ public void build(String outputDir, String appName, String packageName, String m // DESKTOP project project.files.add(new ProjectFile("desktop/build.gradle")); project.files.add(new ProjectFile("desktop/src/DesktopLauncher", - "desktop/src/main/java/" + packageDir + "/desktop/DesktopLauncher.java", true)); + "desktop/src/main/java/com/bladecoder/engine/DesktopLauncher.java", true)); - project.files.add(new ProjectFile("desktop/src/icons/icon16.png", "desktop/src/main/resources/icons/icon16.png", false)); - project.files.add(new ProjectFile("desktop/src/icons/icon32.png", "desktop/src/main/resources/icons/icon32.png", false)); - project.files.add(new ProjectFile("desktop/src/icons/icon128.png", "desktop/src/main/resources/icons/icon128.png", false)); + project.files.add( + new ProjectFile("desktop/src/icons/icon16.png", "desktop/src/main/resources/icons/icon16.png", false)); + project.files.add( + new ProjectFile("desktop/src/icons/icon32.png", "desktop/src/main/resources/icons/icon32.png", false)); + project.files.add(new ProjectFile("desktop/src/icons/icon128.png", + "desktop/src/main/resources/icons/icon128.png", false)); // Assets String assetPath = "assets"; @@ -331,7 +228,7 @@ public void build(String outputDir, String appName, String packageName, String m project.files.add(new ProjectFile("android/res/drawable-xxhdpi/ic_launcher.png", false)); project.files.add(new ProjectFile("android/res/drawable-xxxhdpi/ic_launcher.png", false)); project.files.add(new ProjectFile("android/src/AndroidLauncher", - "android/src/main/java/" + packageDir + "/AndroidLauncher.java", true)); + "android/src/main/java/com/bladecoder/engine/AndroidLauncher.java", true)); project.files.add(new ProjectFile("android/AndroidManifest.xml")); project.files.add(new ProjectFile("android/src/expansion/AndroidManifest.xml")); project.files.add(new ProjectFile("android/build.gradle", true)); @@ -343,8 +240,8 @@ public void build(String outputDir, String appName, String packageName, String m project.files.add(new ProjectFile("local.properties", true)); // IOS ROBOVM - project.files.add( - new ProjectFile("ios/src/IOSLauncher", "ios/src/main/java/" + packageDir + "/IOSLauncher.java", true)); + project.files.add(new ProjectFile("ios/src/IOSLauncher", + "ios/src/main/java/com/bladecoder/engine/IOSLauncher.java", true)); project.files.add(new ProjectFile("ios/data/Default.png", false)); project.files.add(new ProjectFile("ios/data/Default@2x.png", false)); project.files.add(new ProjectFile("ios/data/Default@2x~ipad.png", false)); @@ -355,7 +252,6 @@ public void build(String outputDir, String appName, String packageName, String m project.files.add(new ProjectFile("ios/data/Default-1024w-1366h@2x~ipad.png", false)); project.files.add(new ProjectFile("ios/data/Default-375w-812h@3x.png", false)); - project.files.add(new ProjectFile("ios/data/Media.xcassets/Contents.json", false)); project.files .add(new ProjectFile("ios/data/Media.xcassets/AppIcon.appiconset/app-store-icon-1024@1x.png", false)); @@ -398,7 +294,7 @@ public void build(String outputDir, String appName, String packageName, String m project.files.add(new ProjectFile("ios/robovm.properties")); project.files.add(new ProjectFile("ios/robovm.xml", true)); - Map values = new HashMap(); + Map values = new HashMap<>(); values.put("%APP_NAME%", appName); values.put("%APP_NAME_ESCAPED%", appName.replace("'", "\\'")); values.put("%PACKAGE%", packageName); @@ -410,7 +306,6 @@ public void build(String outputDir, String appName, String packageName, String m values.put("%ANDROID_SDK%", sdkPath); values.put("%ASSET_PATH%", assetPath); - values.put("%BUILD_TOOLS_VERSION%", Versions.getBuildToolsVersion()); values.put("%API_LEVEL%", Versions.getAndroidAPILevel()); values.put("%BLADE_ENGINE_VERSION%", Versions.getVersion()); @@ -534,7 +429,7 @@ private void copyFile(ProjectFile file, File out, Map values) th throw new RuntimeException("Couldn't create dir '" + outFile.getAbsolutePath() + "'"); } - boolean isTemp = file instanceof TemporaryProjectFile ? true : false; + boolean isTemp = file instanceof TemporaryProjectFile; if (file.isTemplate) { String txt; diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/ActorPanel.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/ActorPanel.java index a2117feae..4aa4c1d01 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/ActorPanel.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/ActorPanel.java @@ -47,74 +47,75 @@ public ActorPanel(Skin skin) { faList = new SpriteList(skin); // props = new ActorProps(skin); - setContent(tabPanel); tabPanel.addTab("Verbs", verbList); - Ctx.project.addPropertyChangeListener(Project.NOTIFY_ACTOR_SELECTED, - new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent e) { - BaseActor a = (BaseActor) e.getNewValue(); + Ctx.project.addPropertyChangeListener(Project.NOTIFY_ACTOR_SELECTED, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent e) { + BaseActor a = (BaseActor) e.getNewValue(); - String selTitle = tabPanel.getSelectedIndex() == -1? null: tabPanel.getTitleAt(tabPanel.getSelectedIndex()); - tabPanel.clear(); - - tabPanel.addTab("Verbs", verbList); + String selTitle = tabPanel.getSelectedIndex() == -1 ? null + : tabPanel.getTitleAt(tabPanel.getSelectedIndex()); + tabPanel.clear(); - if (a != null) { - - if (a instanceof SpriteActor && ((SpriteActor) a).getRenderer() instanceof AnimationRenderer) - tabPanel.addTab("Animations", faList); + tabPanel.addTab("Verbs", verbList); + + if (a != null) { + + if (a instanceof SpriteActor && ((SpriteActor) a).getRenderer() instanceof AnimationRenderer) + tabPanel.addTab("Animations", faList); + + if (a instanceof CharacterActor) { + tabPanel.addTab("Simple Dialogs", dialogList); + } - if (a instanceof CharacterActor) { - tabPanel.addTab("Dialogs", dialogList); - } - - // tabPanel.addTab("Actor Props", props); - setTile("ACTOR " + a.getId()); - - // select previous selected tab - if (selTitle != null) { - for (int i = 0; i < tabPanel.getTabCount(); i++) { - if (tabPanel.getTitleAt(i).equals(selTitle)) { - tabPanel.setTab(i); - } - } + setTile("ACTOR " + a.getId()); + + // select previous selected tab + if (selTitle != null) { + for (int i = 0; i < tabPanel.getTabCount(); i++) { + if (tabPanel.getTitleAt(i).equals(selTitle)) { + tabPanel.setTab(i); } - } else { - setTile("ACTOR"); - } - - if(a instanceof SpriteActor && ((SpriteActor) a).getRenderer() instanceof AnimationRenderer) { - HashMap anims = ((AnimationRenderer)((SpriteActor) a).getRenderer()).getAnimations(); - if(anims != null) - faList.addElements((SpriteActor)a, Arrays.asList(anims.values().toArray(new AnimationDesc[0]))); - else - faList.addElements((SpriteActor)a, null); - } else { - faList.addElements(null, null); } - - verbList.changeActor(); - - if(a instanceof CharacterActor) { - - HashMap dialogs = ((CharacterActor) a).getDialogs(); - if(dialogs != null) - dialogList.addElements((CharacterActor)a, Arrays.asList(dialogs.values().toArray(new Dialog[0]))); - else - dialogList.addElements((CharacterActor)a, null); - } else { - dialogList.addElements(null, null); - } - -// props.setActorDocument(a); - } + } else { + setTile("ACTOR"); + } + + if (a instanceof SpriteActor && ((SpriteActor) a).getRenderer() instanceof AnimationRenderer) { + HashMap anims = ((AnimationRenderer) ((SpriteActor) a).getRenderer()) + .getAnimations(); + if (anims != null) + faList.addElements((SpriteActor) a, + Arrays.asList(anims.values().toArray(new AnimationDesc[0]))); + else + faList.addElements((SpriteActor) a, null); + } else { + faList.addElements(null, null); + } + + verbList.changeActor(); + + if (a instanceof CharacterActor) { + + HashMap dialogs = ((CharacterActor) a).getDialogs(); + if (dialogs != null) + dialogList.addElements((CharacterActor) a, + Arrays.asList(dialogs.values().toArray(new Dialog[0]))); + else + dialogList.addElements((CharacterActor) a, null); + } else { + dialogList.addElements(null, null); + } + +// props.setActorDocument(a); + + } + + }); - }); - } } diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/CreateAndroidKeystoreDialog.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/CreateAndroidKeystoreDialog.java new file mode 100644 index 000000000..fdc65c226 --- /dev/null +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/CreateAndroidKeystoreDialog.java @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright 2014 Rafael Garcia Moreno. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package com.bladecoder.engineeditor.ui; + +import java.util.Arrays; + +import com.badlogic.gdx.scenes.scene2d.ui.Skin; +import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; +import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener.ChangeEvent; +import com.bladecoder.engineeditor.common.Message; +import com.bladecoder.engineeditor.common.RunProccess; +import com.bladecoder.engineeditor.ui.panels.EditDialog; +import com.bladecoder.engineeditor.ui.panels.FileInputPanel; +import com.bladecoder.engineeditor.ui.panels.InputPanel; +import com.bladecoder.engineeditor.ui.panels.InputPanelFactory; + +public class CreateAndroidKeystoreDialog extends EditDialog { + + private static final String INFO = "Create the keystore needed to sign Android packages."; + + private InputPanel keyStoreFile; + private InputPanel androidKeyAlias; + private InputPanel androidKeyStorePassword; + private InputPanel androidKeyAliasPassword; + + protected ChangeListener listener; + + public CreateAndroidKeystoreDialog(Skin skin) { + super("CREATE KEY STORE FOR ANDROID", skin); + + keyStoreFile = new FileInputPanel(skin, "Select the key store", "Select the key store file name and location", + FileInputPanel.DialogType.SAVE_FILE); + + androidKeyAlias = InputPanelFactory.createInputPanel(skin, "KeyAlias", "Select the Key ID/Alias", true); + + androidKeyStorePassword = InputPanelFactory.createInputPanel(skin, "KeyStorePasswd", "Key Store Password", + true); + androidKeyAliasPassword = InputPanelFactory.createInputPanel(skin, "KeyAliasPasswd", "Key Alias Password", + true); + + addInputPanel(keyStoreFile); + addInputPanel(androidKeyAlias); + addInputPanel(androidKeyStorePassword); + addInputPanel(androidKeyAliasPassword); + + setInfo(INFO); + } + + @Override + protected void ok() { + createKeyStore(); + } + + @Override + protected boolean validateFields() { + boolean ok = true; + + if (!keyStoreFile.validateField()) + ok = false; + + if (!androidKeyAlias.validateField()) + ok = false; + + if (androidKeyStorePassword.getText() == null || androidKeyStorePassword.getText().length() < 6) { + Message.showMsgDialog(getStage(), "Error", "Keystore password must be at least 6 character long"); + ok = false; + + return false; + } + + if (androidKeyAliasPassword.getText() == null || androidKeyAliasPassword.getText().length() < 6) { + Message.showMsgDialog(getStage(), "Error", "Key password must be at least 6 character long"); + ok = false; + } + + return ok; + } + + private void createKeyStore() { + // keytool -genkey -v -keystore my-release-key.keystore -alias alias_name + // -keyalg RSA -keysize 2048 -validity 10000 + + String[] args = { "-genkey", "-noprompt", "-v", "-keystore", getKeyStorePath(), "-alias", getKeyAlias(), + "-keyalg", "RSA", "-keysize", "2048", "-validity", "10000", "-storepass", getKeyStorePassword(), + "-dname", "CN=bladeengine.com", "-keypass", getKeyAliasPassword() }; + + try { + Process p = RunProccess.runJavaHomeBin("keytool", Arrays.asList(args)); + p.waitFor(); + + if (p.exitValue() == 0) { + if (listener != null) + listener.changed(new ChangeEvent(), this); + } else { + Message.showMsgDialog(getStage(), "Error", "Error generating key"); + cancel(); + } + } catch (Exception e) { + Message.showMsgDialog(getStage(), "Error", e.getMessage()); + cancel(); + } + + } + + public String getKeyStorePath() { + return keyStoreFile.getText(); + } + + public String getKeyAlias() { + return androidKeyAlias.getText(); + } + + public String getKeyStorePassword() { + return androidKeyStorePassword.getText(); + } + + public String getKeyAliasPassword() { + return androidKeyAliasPassword.getText(); + } + + public void setListener(ChangeListener l) { + listener = l; + } +} diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/CreateAtlasDialog.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/CreateAtlasDialog.java index cd01ca820..d314bd325 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/CreateAtlasDialog.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/CreateAtlasDialog.java @@ -160,7 +160,7 @@ else if (fMag.equals("MipMapNearestNearest")) try { ImageUtils.createAtlas(dir.getText(), outdir + "/" + r, name + ".atlas", scale, (int) (maxW * scale), - (int) (maxH * scale), filterMin, filterMag, outputFormat.getText()); + (int) (maxH * scale), filterMin, filterMag, outputFormat.getText(), true); } catch (IOException e) { EditorLogger.error(e.getMessage()); Message.showMsgDialog(getStage(), "Error creating atlas", e.getMessage()); diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/CreateResolutionDialog.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/CreateResolutionDialog.java index c7f023e99..4dfdd6b4a 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/CreateResolutionDialog.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/CreateResolutionDialog.java @@ -18,6 +18,7 @@ import java.io.File; import java.io.IOException; +import com.badlogic.gdx.scenes.scene2d.Stage; import com.badlogic.gdx.scenes.scene2d.ui.Skin; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener.ChangeEvent; @@ -37,76 +38,78 @@ public class CreateResolutionDialog extends EditDialog { private static final String INFO = "Create a new resolution. Scale all atlases and images of the game."; private InputPanel scale; - + protected ChangeListener listener; - + String atlasDir = Ctx.project.getAssetPath() + Project.ATLASES_PATH; String uiDir = Ctx.project.getAssetPath() + Project.UI_PATH; String imageDir = Ctx.project.getAssetPath() + Project.IMAGE_PATH; public CreateResolutionDialog(Skin skin) { super("CREATE RESOLUTION", skin); - - scale = InputPanelFactory.createInputPanel(skin, "Scale", - "Scale relative to the world resolution", Param.Type.FLOAT, true); + + scale = InputPanelFactory.createInputPanel(skin, "Scale", "Scale relative to the world resolution", + Param.Type.FLOAT, true); addInputPanel(scale); - + setInfo(INFO); } @Override protected void ok() { - - Message.showMsg(getStage(), "Creating resolution...", true); - + + final Stage stage = getStage(); + + Message.showMsg(stage, "Creating resolution...", true); + Timer.schedule(new Task() { @Override - public void run() { + public void run() { createResolution(); - String msg = scaleImages(); - - if(listener != null) + String msg = scaleImages(); + + if (listener != null) listener.changed(new ChangeEvent(), CreateResolutionDialog.this); - + Message.hideMsg(); - - if(msg != null) - Message.showMsgDialog(getStage(), "Error creating resolution", msg); + + if (msg != null) + Message.showMsgDialog(stage, "Error creating resolution", msg); } - },1); + }, 1); } - + private void createResolution() { // float s = Float.parseFloat(scale.getText()); // String prefix = (int)(Ctx.project.getWorld().getWidth() * s) + "_" + (int)(Ctx.project.getWorld().getHeight() * s); String prefix = scale.getText().trim(); - + new File(atlasDir + "/" + prefix).mkdir(); new File(uiDir + "/" + prefix).mkdir(); - new File(imageDir + "/" + prefix).mkdir(); + new File(imageDir + "/" + prefix).mkdir(); } private String scaleImages() { - + float s = Float.parseFloat(scale.getText()); // String prefix = (int)(Ctx.project.getWorld().getWidth() * s) + "_" + (int)(Ctx.project.getWorld().getHeight() * s); String prefix = scale.getText().trim(); - + // COPY ASSETS FROM WORLD RESOLUTION SCALED String wPrefix = Ctx.project.getResDir(); - + try { ImageUtils.scaleDirFiles(new File(uiDir + "/" + wPrefix), new File(uiDir + "/" + prefix), s); ImageUtils.scaleDirFiles(new File(imageDir + "/" + wPrefix), new File(imageDir + "/" + prefix), s); - + ImageUtils.scaleDirAtlases(new File(atlasDir + "/" + wPrefix), new File(atlasDir + "/" + prefix), s); ImageUtils.scaleDirAtlases(new File(uiDir + "/" + wPrefix), new File(uiDir + "/" + prefix), s); } catch (IOException e) { return e.getMessage(); } - + return null; } diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/EditActorDialog.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/EditActorDialog.java index 2c9f1616a..01df026de 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/EditActorDialog.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/EditActorDialog.java @@ -79,8 +79,8 @@ public class EditActorDialog extends EditModelDialog { "Define the walkable area for characters", "Obstacle actors forbids zones for walking actors" }; private static final String TEXT_ALIGN[] = { "left", "center", "right" }; - private static final String ORG_ALIGN[] = { "bottom", "center", "left", "right", "top", "botton-right", - "botton-left", "top-right", "top-left" }; + private static final String ORG_ALIGN[] = { "bottom", "center", "left", "right", "top", "bottom-right", + "bottom-left", "top-right", "top-left" }; private InputPanel typePanel; private InputPanel id; @@ -570,6 +570,7 @@ protected void inputsToModel(boolean create) { } } + sa.getRenderer().setWorld(parent.getWorld()); boolean bbfr = Boolean.parseBoolean(bboxFromRenderer.getText()); if (bbfr != sa.isBboxFromRenderer()) diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/EditAnimationDialog.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/EditAnimationDialog.java index 4cbdce432..a637010ca 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/EditAnimationDialog.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/EditAnimationDialog.java @@ -381,8 +381,14 @@ protected void ok() { FilteredSelectBox cb = (FilteredSelectBox) id.getField(); if (e == null && cb.getSelectedIndex() == 0) { + AnimationRenderer renderer = (AnimationRenderer) parent.getRenderer(); + for (int i = 1; i < cb.getItems().size; i++) { cb.setSelectedIndex(i); + + if (renderer.getAnimations().get(id.getText()) != null) + continue; + inputsToModel(true); // doc.setId(e, cb.getItems().get(i)); @@ -486,4 +492,17 @@ protected void modelToInputs() { } } + @Override + protected boolean validateFields() { + AnimationRenderer renderer = (AnimationRenderer) parent.getRenderer(); + HashMap animations = renderer.getAnimations(); + + if ((e == null && animations.get(id.getText()) != null) + || (e != null && animations.get(id.getText()) != null && !e.id.equals(id.getText()))) { + id.setError(true); + return false; + } + + return super.validateFields(); + } } diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/EditDialogDialog.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/EditDialogDialog.java index 796285ae6..704baf970 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/EditDialogDialog.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/EditDialogDialog.java @@ -25,46 +25,45 @@ import com.bladecoder.engineeditor.ui.panels.InputPanelFactory; public class EditDialogDialog extends EditModelDialog { - public static final String INFO = "Actors can have several dialogs defined. Dialogs have a tree of options to choose"; + public static final String INFO = "[RED]DEPRECATED:[]\nActors can have several dialogs defined. Dialogs have a list of options to choose. This is the legacy option to create simple dialogs, for more complex dialogs use the *Ink* language."; private InputPanel id; - - public EditDialogDialog(Skin skin, CharacterActor parent, Dialog e) { + + public EditDialogDialog(Skin skin, CharacterActor parent, Dialog e) { super(skin); - - id = InputPanelFactory.createInputPanel(skin, "Dialog ID", - "Select the dialog id to create.", true); + + id = InputPanelFactory.createInputPanel(skin, "Dialog ID", "Select the dialog id to create.", true); setInfo(INFO); - + init(parent, e, new InputPanel[] { id }); } - + @Override protected void inputsToModel(boolean create) { - - if(create) { + + if (create) { e = new Dialog(); } else { parent.getDialogs().remove(e.getId()); } - - if(parent.getDialogs() != null) + + if (parent.getDialogs() != null) e.setId(ElementUtils.getCheckedId(id.getText(), parent.getDialogs().keySet().toArray(new String[0]))); else e.setId(id.getText()); - + parent.addDialog(e); // TODO UNDO OP // UndoOp undoOp = new UndoAddElement(doc, e); // Ctx.project.getUndoStack().add(undoOp); - + Ctx.project.setModified(); } @Override protected void modelToInputs() { id.setText(e.getId()); - } + } } diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/EditSceneDialog.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/EditSceneDialog.java index 9251b0b85..79ebdd201 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/EditSceneDialog.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/EditSceneDialog.java @@ -37,7 +37,6 @@ import com.bladecoder.engineeditor.Ctx; import com.bladecoder.engineeditor.common.EditorLogger; import com.bladecoder.engineeditor.common.ElementUtils; -import com.bladecoder.engineeditor.common.Message; import com.bladecoder.engineeditor.model.Project; import com.bladecoder.engineeditor.ui.panels.EditModelDialog; import com.bladecoder.engineeditor.ui.panels.FilteredSelectBox; @@ -74,8 +73,7 @@ public EditSceneDialog(Skin skin, World parent, Scene e) { super(skin); - id = InputPanelFactory.createInputPanel(skin, "Scene ID", - "The ID is mandatory for scenes.", true); + id = InputPanelFactory.createInputPanel(skin, "Scene ID", "The ID is mandatory for scenes.", true); backgroundAtlas = InputPanelFactory.createInputPanel(skin, "Background Atlas", "The atlas where the background for the scene is located", Type.ATLAS_ASSET, false); backgroundRegion = InputPanelFactory.createInputPanel(skin, "Background Region Id", @@ -83,16 +81,17 @@ public EditSceneDialog(Skin skin, World parent, Scene e) { // depthVector = InputPanelFactory.createInputPanel(skin, "Depth Vector", // "X: the actor 'y' position for a 0.0 scale, Y: the actor 'y' position for a 1.0 scale.", // Param.Type.VECTOR2, false); - - depthVector = InputPanelFactory.createInputPanel(skin, "Fake depth", "Change actor scale based in the 'y' axis position.", Param.Type.BOOLEAN, true, - "false"); - + + depthVector = InputPanelFactory.createInputPanel(skin, "Fake depth", + "Change actor scale based in the 'y' axis position.", Param.Type.BOOLEAN, true, "false"); + state = InputPanelFactory.createInputPanel(skin, "State", "The initial state for the scene.", false); - music = InputPanelFactory.createInputPanel(skin, "Music Filename", "The music for the scene", Type.MUSIC_ASSET, false); + music = InputPanelFactory.createInputPanel(skin, "Music Filename", "The music for the scene", Type.MUSIC_ASSET, + false); loopMusic = InputPanelFactory.createInputPanel(skin, "Loop Music", "If the music is playing in looping", Param.Type.BOOLEAN, true, "true"); - volumeMusic = InputPanelFactory.createInputPanel(skin, "Music Volume", "The volume of the music. Value is between 0 and 1.", - Param.Type.FLOAT, true, "1"); + volumeMusic = InputPanelFactory.createInputPanel(skin, "Music Volume", + "The volume of the music. Value is between 0 and 1.", Param.Type.FLOAT, true, "1"); initialMusicDelay = InputPanelFactory.createInputPanel(skin, "Initial music delay", "The time to wait before playing", Param.Type.FLOAT, true, "0"); repeatMusicDelay = InputPanelFactory.createInputPanel(skin, "Repeat music delay", @@ -103,8 +102,9 @@ public EditSceneDialog(Skin skin, World parent, Scene e) { sceneSize = InputPanelFactory.createInputPanel(skin, "Scene Dimension", "Sets the size of the scene. If empty, the background image size is used as the scene dimension.", Param.Type.DIMENSION, false); - - walkzone = InputPanelFactory.createInputPanel(skin, "Walkzone", "The initial walkzone.", Type.WALKZONE_ACTOR, false); + + walkzone = InputPanelFactory.createInputPanel(skin, "Walkzone", "The initial walkzone.", Type.WALKZONE_ACTOR, + false); bgImage = new Image(); bgImage.setScaling(Scaling.fit); @@ -118,7 +118,8 @@ public void changed(ChangeEvent event, Actor actor) { try { fillBGRegions(backgroundAtlas, backgroundRegion); } catch (Exception e) { - Message.showMsg(getStage(), "Error loading regions from selected atlas", 4); + EditorLogger.error("Error loading regions from selected atlas: " + backgroundAtlas.getText() + "." + + backgroundRegion.getText()); } } }); @@ -133,7 +134,8 @@ public void changed(ChangeEvent event, Actor actor) { try { fillBGRegions(backgroundAtlas, backgroundRegion); } catch (Exception e2) { - EditorLogger.error("Error loading regions from selected atlas"); + EditorLogger.error("Error loading regions from selected atlas: " + backgroundAtlas.getText() + "." + + backgroundRegion.getText()); } init(parent, e, new InputPanel[] { id, backgroundAtlas, backgroundRegion, depthVector, state, sceneSize, music, @@ -214,19 +216,20 @@ protected void inputsToModel(boolean create) { parent.getScenes().remove(e.getId()); } - e.setId(ElementUtils.getCheckedId(id.getText(), Ctx.project.getWorld().getScenes().keySet().toArray(new String[0]))); + e.setId(ElementUtils.getCheckedId(id.getText(), + Ctx.project.getWorld().getScenes().keySet().toArray(new String[0]))); e.setBackgroundAtlas(backgroundAtlas.getText()); e.setBackgroundRegionId(backgroundRegion.getText()); - + boolean dv = Boolean.parseBoolean(depthVector.getText()); - - if(dv == true && e.getDepthVector() == null) { // create depth vector + + if (dv == true && e.getDepthVector() == null) { // create depth vector e.setDepthVector(new Vector2(Ctx.project.getWorld().getHeight(), 0)); - } else if(dv == false && e.getDepthVector() != null) { // Remove depth vector + } else if (dv == false && e.getDepthVector() != null) { // Remove depth vector e.setDepthVector(null); } - + e.setState(state.getText()); MusicDesc md = null; @@ -245,7 +248,7 @@ protected void inputsToModel(boolean create) { e.setMusicDesc(md); e.setSceneSize(Param.parseVector2(sceneSize.getText())); - + e.setWalkZone(walkzone.getText()); parent.addScene(e); @@ -269,12 +272,12 @@ protected void modelToInputs() { id.setText(e.getId()); backgroundAtlas.setText(e.getBackgroundAtlas()); backgroundRegion.setText(e.getBackgroundRegionId()); - + if (e.getDepthVector() != null) depthVector.setText("true"); else depthVector.setText("false"); - + state.setText(e.getState()); MusicDesc md = e.getMusicDesc(); @@ -290,7 +293,7 @@ protected void modelToInputs() { if (e.getSceneSize() != null) sceneSize.setText(Param.toStringParam(e.getSceneSize())); - + walkzone.setText(e.getWalkZone()); } diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/EditVerbDialog.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/EditVerbDialog.java index 593ccd8d3..bdc6e6653 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/EditVerbDialog.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/EditVerbDialog.java @@ -25,6 +25,7 @@ import com.bladecoder.engine.actions.Param.Type; import com.bladecoder.engine.model.Verb; import com.bladecoder.engine.model.VerbManager; +import com.bladecoder.engine.util.ActionUtils; import com.bladecoder.engineeditor.Ctx; import com.bladecoder.engineeditor.ui.panels.EditModelDialog; import com.bladecoder.engineeditor.ui.panels.EditableSelectBox; @@ -34,11 +35,10 @@ import com.bladecoder.engineeditor.ui.panels.ScopePanel; public class EditVerbDialog extends EditModelDialog { - private static final String VERBS[] = { "lookat", "pickup", "talkto", "use", - "leave", "action", "enter", "exit" }; + private static final String VERBS[] = { "lookat", "pickup", "talkto", "use", "leave", "action", "enter", "exit" }; private static final String SCENE_VERBS[] = { "init", "test" }; - + private static final String DEFAULT_DESC = "Verbs are used to create the game interaction. Select or write the verb to create"; private static final String CUSTOM_VERB_DESC = "User defined verbs can be called\n from dialogs or inside other verbs using \nthe 'run_verb' action"; @@ -50,36 +50,30 @@ public class EditVerbDialog extends EditModelDialog { "Called when the user clicks\n in an exit zone in scene", "Called when the user clicks in the actor. Useful for one click actions. Doesn't show the verb menu.", "Called when the player enters\n in the object bounding box", - "Called when the player exits\n the object bounding box"}; + "Called when the player exits\n the object bounding box" }; + + private static final String SCENE_VERBS_INFO[] = { "Called every time\n that the scene is loaded", + "Called every time\n that the scene is loaded in test mode.\n'test' verb is called before the 'init' verb" }; - private static final String SCENE_VERBS_INFO[] = { - "Called every time\n that the scene is loaded", - "Called every time\n that the scene is loaded in test mode.\n'test' verb is called before the 'init' verb"}; - private InputPanel id; private InputPanel state; private InputPanel target; private InputPanel icon; - + private String scope; - public EditVerbDialog(Skin skin, String scope, VerbManager parentElement, - Verb e) { + public EditVerbDialog(Skin skin, String scope, VerbManager parentElement, Verb e) { super(skin); - + this.scope = scope; - - id = InputPanelFactory.createInputPanel(skin, "Verb ID", - "Select the verb to create.", Type.EDITABLE_OPTION, true, - "", ScopePanel.SCENE_SCOPE.equals(scope) ? SCENE_VERBS : VERBS); - state = InputPanelFactory.createInputPanel(skin, "State", - "Select the state."); - target = InputPanelFactory.createInputPanel(skin, - "Target BaseActor", + + id = InputPanelFactory.createInputPanel(skin, "Verb ID", "Select the verb to create.", Type.EDITABLE_OPTION, + true, "", ScopePanel.SCENE_SCOPE.equals(scope) ? SCENE_VERBS : VERBS); + state = InputPanelFactory.createInputPanel(skin, "State", "Select the state."); + target = InputPanelFactory.createInputPanel(skin, "Target BaseActor", "Select the target actor id for the 'use' verb."); - - icon = InputPanelFactory.createInputPanel(skin, - "UI Icon", + + icon = InputPanelFactory.createInputPanel(skin, "UI Icon", "The icon that will be showed in the ui for selecting the verb."); if (ScopePanel.SCENE_SCOPE.equals(scope)) @@ -87,22 +81,21 @@ public EditVerbDialog(Skin skin, String scope, VerbManager parentElement, else setInfo(VERBS_INFO[0]); - id.getField() - .addListener(new ChangeListener() { + id.getField().addListener(new ChangeListener() { + + @Override + public void changed(ChangeEvent event, Actor actor) { + updateDesc(); + } - @Override - public void changed(ChangeEvent event, Actor actor) { - updateDesc(); - } + }); - }); - - ((EditableSelectBox)id.getField()).getInput().setTextFieldListener(new TextFieldListener() { + ((EditableSelectBox) id.getField()).getInput().setTextFieldListener(new TextFieldListener() { @Override public void keyTyped(TextField actor, char c) { updateDesc(); } - }); + }); init(parentElement, e, new InputPanel[] { id, state, target, icon }); @@ -114,17 +107,16 @@ public void keyTyped(TextField actor, char c) { if (id.equals("use")) setVisible(target, true); } - + updateDesc(); } - + private void updateDesc() { - String idStr = (String) id.getText(); - int i = ((OptionsInputPanel) id) - .getSelectedIndex(); + String idStr = id.getText(); + int i = ((OptionsInputPanel) id).getSelectedIndex(); - if(i == -1) { - if(idStr != null && idStr.isEmpty()) + if (i == -1) { + if (idStr != null && idStr.isEmpty()) setInfo(DEFAULT_DESC); else setInfo(CUSTOM_VERB_DESC); @@ -132,7 +124,7 @@ private void updateDesc() { if (ScopePanel.SCENE_SCOPE.equals(scope)) { setInfo(SCENE_VERBS_INFO[i]); } else { - setInfo(VERBS_INFO[i]); + setInfo(VERBS_INFO[i]); } } @@ -141,26 +133,25 @@ private void updateDesc() { else setVisible(target, false); - pack(); - } + pack(); + } @Override protected void inputsToModel(boolean create) { - - if(create) { + + if (create) { e = new Verb(); } else { HashMap verbs = parent.getVerbs(); verbs.remove(e.getHashKey()); } - + e.setId(id.getText()); e.setState(state.getText()); e.setTarget(target.getText()); e.setIcon(icon.getText()); - - parent.addVerb(e); + parent.addVerb(e); // TODO UNDO OP // UndoOp undoOp = new UndoAddElement(doc, e); @@ -175,5 +166,28 @@ protected void modelToInputs() { state.setText(e.getState()); target.setText(e.getTarget()); icon.setText(e.getIcon()); - } + } + + @Override + protected boolean validateFields() { + + if (!super.validateFields()) + return false; + + Verb verb = parent.getVerb(id.getText(), state.getText(), target.getText()); + + if (verb == null) + return true; + + boolean sameParams = verb.getId().equals(id.getText()) + && ActionUtils.compareNullStr(verb.getState(), state.getText()) + && ActionUtils.compareNullStr(verb.getTarget(), target.getText()); + + if (sameParams && (e == null || verb != e)) { + id.setError(true); + return false; + } + + return true; + } } diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/PackageDialog.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/PackageDialog.java index ef0321858..5599737cc 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/PackageDialog.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/PackageDialog.java @@ -16,6 +16,7 @@ package com.bladecoder.engineeditor.ui; import java.io.File; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FilenameFilter; @@ -28,10 +29,15 @@ import org.apache.commons.io.FileUtils; import com.badlogic.gdx.scenes.scene2d.Actor; +import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.Stage; +import com.badlogic.gdx.scenes.scene2d.ui.Cell; import com.badlogic.gdx.scenes.scene2d.ui.Skin; +import com.badlogic.gdx.scenes.scene2d.ui.Table; +import com.badlogic.gdx.scenes.scene2d.ui.TextButton; import com.badlogic.gdx.scenes.scene2d.ui.TextField; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; +import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; import com.badlogicgames.packr.Packr; import com.badlogicgames.packr.PackrConfig; import com.badlogicgames.packr.PackrConfig.Platform; @@ -55,14 +61,18 @@ public class PackageDialog extends EditDialog { private static final String DESKTOP_LAUNCHER = "DesktopLauncher.java"; - private static final String INFO = "Package the Adventure for distribution"; + private static final String INFO = "Package the Game for distribution"; private static final String[] ARCHS = { "desktop", "android", "ios" }; - private static final String[] TYPES = { "Bundle JRE", "Runnable jar" }; - private static final String[] OSS = { "all", "windows32", "windows64", "linux64", "linux32", "macOSX" }; + private static final String[] DESKTOP_TYPES = { "Bundle JRE", "Runnable jar" }; + private static final String[] ANDROID_TYPES = { ".apk", ".aab" }; + private static final String[] OSS = { "all", "windows32", "windows64", "linux64", "linux32", "macOS" }; private InputPanel arch; private InputPanel dir; - private InputPanel type; + + private InputPanel desktopType; + private InputPanel androidType; + private InputPanel os; private FileInputPanel linux64JRE; private FileInputPanel linux32JRE; @@ -81,20 +91,21 @@ public class PackageDialog extends EditDialog { private InputPanel iosSignIdentity; private InputPanel iosProvisioningProfile; - + private InputPanel customBuildParameters; private InputPanel[] options; @SuppressWarnings("unchecked") - public PackageDialog(Skin skin) { - super("PACKAGE ADVENTURE", skin); + public PackageDialog(final Skin skin) { + super("PACKAGE GAME", skin); arch = InputPanelFactory.createInputPanel(skin, "Architecture", "Select the target Architecture for the game", ARCHS, true); dir = new FileInputPanel(skin, "Output Directory", "Select the output directory to put the package", FileInputPanel.DialogType.DIRECTORY); - type = InputPanelFactory.createInputPanel(skin, "Type", "Select the package type", TYPES, true); + desktopType = InputPanelFactory.createInputPanel(skin, "Type", "Select the package type", DESKTOP_TYPES, true); + androidType = InputPanelFactory.createInputPanel(skin, "Type", "Select the package type", ANDROID_TYPES, true); os = InputPanelFactory.createInputPanel(skin, "OS", "Select the OS of the package", OSS, true); FileTypeFilter typeFilter = new FileTypeFilter(true); @@ -137,7 +148,7 @@ public PackageDialog(Skin skin) { Param.Type.BOOLEAN, true, "false"); androidKeyStore = new FileInputPanel(skin, "KeyStore", "Select the Key Store Location", FileInputPanel.DialogType.OPEN_FILE); - androidKeyAlias = InputPanelFactory.createInputPanel(skin, "KeyAlias", "Select the Key Alias Location", true); + androidKeyAlias = InputPanelFactory.createInputPanel(skin, "KeyAlias", "Select the Key Alias", true); androidKeyStorePassword = InputPanelFactory.createInputPanel(skin, "KeyStorePasswd", "Key Store Password", true); @@ -150,13 +161,13 @@ public PackageDialog(Skin skin) { iosProvisioningProfile = InputPanelFactory.createInputPanel(skin, "Provisioning Profile", "Empty for auto select.", false); - + customBuildParameters = InputPanelFactory.createInputPanel(skin, "Custom build parameters", "You can add extra build parameters for customized build scripts.", false); - options = new InputPanel[] { type, os, linux64JRE, linux32JRE, winJRE, winJRE64, osxJRE, version, icon, - versionCode, androidSDK, expansionFile, androidKeyStore, androidKeyAlias, iosSignIdentity, - iosProvisioningProfile, customBuildParameters }; + options = new InputPanel[] { androidType, desktopType, os, linux64JRE, linux32JRE, winJRE, winJRE64, osxJRE, + version, icon, versionCode, androidSDK, expansionFile, androidKeyStore, androidKeyAlias, + iosSignIdentity, iosProvisioningProfile, customBuildParameters }; addInputPanel(arch); addInputPanel(dir); @@ -199,10 +210,10 @@ public void changed(ChangeEvent event, Actor actor) { } }); - ((FilteredSelectBox) (type.getField())).addListener(new ChangeListener() { + ((FilteredSelectBox) (desktopType.getField())).addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { - typeChanged(); + desktopTypeChanged(); } }); @@ -213,14 +224,51 @@ public void changed(ChangeEvent event, Actor actor) { } }); + ((FilteredSelectBox) (androidType.getField())).addListener(new ChangeListener() { + @Override + public void changed(ChangeEvent event, Actor actor) { + androidTypeChanged(); + } + }); + + // Add the 'create' button to the keystore. + TextButton createButton = new TextButton("Create", skin, "no-toggled"); + + createButton.addListener(new ClickListener() { + @Override + public void clicked(InputEvent event, float x, float y) { + final CreateAndroidKeystoreDialog c = new CreateAndroidKeystoreDialog(skin); + c.show(getStage()); + + c.setListener(new ChangeListener() { + @Override + public void changed(ChangeEvent event, Actor actor) { + androidKeyStore.setText(c.getKeyStorePath()); + androidKeyAlias.setText(c.getKeyAlias()); + androidKeyStorePassword.setText(c.getKeyStorePassword()); + androidKeyAliasPassword.setText(c.getKeyAliasPassword()); + + } + }); + + } + }); + + Table t = new Table(); + Actor a = androidKeyStore.getField(); + Cell c = androidKeyStore.getCell(a); + t.add(a); + t.add(createButton); + c.setActor(t); + archChanged(); } @Override protected void ok() { - + final Stage stg = getStage(); - + Message.showMsg(stg, "Generating package...", true); new Thread() { @@ -245,7 +293,7 @@ public void run() { } catch (Exception ex) { msg = "Something went wrong while saving the project.\n\n" + ex.getClass().getSimpleName() + " - " + ex.getMessage(); - + Message.showMsgDialog(stg, "Error", msg); return; } @@ -264,15 +312,14 @@ public void run() { if (i.getText() != null) Ctx.project.getEditorConfig().setProperty("package." + i.getTitle(), i.getText()); } - - + // hide message Message.hideMsg(); if (msg != null) { final String m = msg; Message.showMsgDialog(stg, "Result", m); - } + } } }.start(); @@ -287,144 +334,174 @@ private String packageAdv() throws IOException { String versionParam = "-Pversion=" + version.getText() + " "; Ctx.project.getProjectConfig().setProperty(Config.VERSION_PROP, version.getText()); - - String customBuildParams = customBuildParameters.getText() == null? "": customBuildParameters.getText() + " "; + + String customBuildParams = customBuildParameters.getText() == null ? "" : customBuildParameters.getText() + " "; if (arch.getText().equals("desktop")) { - String jarDir = Ctx.project.getProjectDir().getAbsolutePath() + "/desktop/build/libs/"; - String jarName = projectName + "-desktop-" + version.getText() + ".jar"; - - String error = genDesktopJar(projectName, versionParam, jarDir, jarName, customBuildParams); - - if (error != null) - msg = error; - - if (type.getText().equals(TYPES[0])) { // BUNDLE JRE - String launcher = getDesktopMainClass(); - - if (os.getText().equals("linux64")) { - packr(Platform.Linux64, linux64JRE.getText(), projectName, jarDir + jarName, launcher, - dir.getText()); - } else if (os.getText().equals("linux32")) { - packr(Platform.Linux32, linux32JRE.getText(), projectName, jarDir + jarName, launcher, - dir.getText()); - } else if (os.getText().equals("windows32")) { - packr(Platform.Windows32, winJRE.getText(), projectName, jarDir + jarName, launcher, dir.getText()); - } else if (os.getText().equals("windows64")) { - packr(Platform.Windows64, winJRE64.getText(), projectName, jarDir + jarName, launcher, - dir.getText()); - } else if (os.getText().equals("macOSX")) { - packr(Platform.MacOS, osxJRE.getText(), projectName, jarDir + jarName, launcher, dir.getText()); - } else if (os.getText().equals("all")) { - packr(Platform.Linux64, linux64JRE.getText(), projectName, jarDir + jarName, launcher, - dir.getText()); - packr(Platform.Linux32, linux32JRE.getText(), projectName, jarDir + jarName, launcher, - dir.getText()); - packr(Platform.Windows32, winJRE.getText(), projectName, jarDir + jarName, launcher, dir.getText()); - packr(Platform.Windows64, winJRE64.getText(), projectName, jarDir + jarName, launcher, - dir.getText()); - packr(Platform.MacOS, osxJRE.getText(), projectName, jarDir + jarName, launcher, dir.getText()); - } - } - } else if (arch.getText().equals("android")) { - String params = versionParam + customBuildParams + "-PversionCode=" + versionCode.getText() + " " + "-Pkeystore=" - + androidKeyStore.getText() + " " + "-PstorePassword=" + androidKeyStorePassword.getText() + " " - + "-Palias=" + androidKeyAlias.getText() + " " + "-PkeyPassword=" - + androidKeyAliasPassword.getText() + " "; - - // UPDATE 'local.properties' with the android SDK location. - if (androidSDK.getText() != null && !androidSDK.getText().trim().isEmpty()) { - String sdk = androidSDK.getText(); - - Properties p = new Properties(); - p.setProperty("sdk.dir", sdk); - p.store(new FileOutputStream( - new File(Ctx.project.getProjectDir().getAbsolutePath(), "local.properties")), null); - } - - if(!new File(Ctx.project.getProjectDir().getAbsolutePath(), "local.properties").exists() && System.getenv("ANDROID_HOME") == null) { - return "You have to specify the Android SDK path or set the ANDROID_HOME environtment variable."; - } + String error = createDesktop(projectName, versionParam, customBuildParams); + return error == null ? msg : error; + } - String task = "android:assembleFullRelease"; - String apk = Ctx.project.getProjectDir().getAbsolutePath() - + "/android/build/outputs/apk/full/release/android-full-release.apk"; - - boolean genExpansion = Boolean.parseBoolean(expansionFile.getText()); - boolean newProjectStructure = new File(Ctx.project.getProjectDir().getAbsolutePath() - + "/assets/").exists(); - - if(!newProjectStructure && genExpansion) - return "You need to update your project to the new layout to generate expansion files."; - - if(!newProjectStructure) { - task = "android:assembleRelease"; - apk = Ctx.project.getProjectDir().getAbsolutePath() - + "/android/build/outputs/apk/android-release.apk"; - } + if (arch.getText().equals("android")) { + String error = createAndroid(projectName, versionParam, customBuildParams); + return error == null ? msg : error; + } - if (genExpansion) { - task = "android:assembleExpansionRelease"; - apk = Ctx.project.getProjectDir().getAbsolutePath() - + "/android/build/outputs/apk/expansion/release/android-expansion-release.apk"; - } + if (arch.getText().equals("ios")) { + String error = createIOS(projectName, customBuildParams); + return error == null ? msg : error; + } - if (RunProccess.runGradle(Ctx.project.getProjectDir(), params + task)) { - File f = new File(apk); - FileUtils.copyFile(f, new File(dir.getText(), projectName + "-" + version.getText() + ".apk")); + return msg; + } - if (genExpansion) { - File fExp = findObb(Ctx.project.getProjectDir().getAbsolutePath() + "/android/build/distributions/", - versionCode.getText()); - FileUtils.copyFile(fExp, new File(dir.getText(), fExp.getName())); - } + private String createIOS(String projectName, String customBuildParams) throws IOException, FileNotFoundException { + if (!System.getProperty("os.name").toLowerCase().contains("mac")) { + return "You need a MacOSX computer with XCode installed to generate the IOS package."; + } - } else { - msg = "Error Generating package"; - } - } else if (arch.getText().equals("ios")) { + // UPDATE 'robovm.properties' + Properties p = new Properties(); + p.load(new FileReader(Ctx.project.getProjectDir().getAbsolutePath() + "/ios/robovm.properties")); + p.setProperty("app.version", version.getText()); + p.setProperty("app.build", versionCode.getText()); + p.setProperty("app.name", Ctx.project.getTitle()); + p.store(new FileOutputStream(new File(Ctx.project.getProjectDir().getAbsolutePath(), "/ios/robovm.properties")), + null); - if (!System.getProperty("os.name").toLowerCase().contains("mac")) { - return "You need a MacOSX computer with XCode installed to generate the IOS package."; - } + List params = new ArrayList<>(); + + if (iosSignIdentity.getText() != null) + params.add("-Probovm.iosSignIdentity=" + iosSignIdentity.getText()); + + if (iosProvisioningProfile.getText() != null) + params.add("-Probovm.iosProvisioningProfile=" + iosProvisioningProfile.getText()); + + if (customBuildParameters.getText() != null) + params.add(customBuildParams); + + // Add clean target in IOS because the app. is not signing well if not cleaning. + params.add("ios:clean"); + + params.add("ios:createIPA"); + + if (RunProccess.runGradle(Ctx.project.getProjectDir(), params)) { + + String apk = Ctx.project.getProjectDir().getAbsolutePath() + "/ios/build/robovm/IOSLauncher.ipa"; + + File f = new File(apk); + FileUtils.copyFile(f, new File(dir.getText(), projectName + "-" + version.getText() + ".ipa")); + } else { + return "Error Generating package"; + } + + return null; + } + + private String createAndroid(String projectName, String versionParam, String customBuildParams) + throws IOException, FileNotFoundException { + String params = versionParam + customBuildParams + "-PversionCode=" + versionCode.getText() + " " + + "-Pkeystore=\"" + androidKeyStore.getText() + "\" " + "-PstorePassword=" + + androidKeyStorePassword.getText() + " " + "-Palias=" + androidKeyAlias.getText() + " " + + "-PkeyPassword=" + androidKeyAliasPassword.getText() + " "; + + // UPDATE 'local.properties' with the android SDK location. + if (androidSDK.getText() != null && !androidSDK.getText().trim().isEmpty()) { + String sdk = androidSDK.getText(); - // UPDATE 'robovm.properties' Properties p = new Properties(); - p.load(new FileReader(Ctx.project.getProjectDir().getAbsolutePath() + "/ios/robovm.properties")); - p.setProperty("app.version", version.getText()); - p.setProperty("app.build", versionCode.getText()); - p.setProperty("app.name", Ctx.project.getTitle()); - p.store(new FileOutputStream( - new File(Ctx.project.getProjectDir().getAbsolutePath(), "/ios/robovm.properties")), null); + p.setProperty("sdk.dir", sdk); + p.store(new FileOutputStream(new File(Ctx.project.getProjectDir().getAbsolutePath(), "local.properties")), + null); + } + + if (!new File(Ctx.project.getProjectDir().getAbsolutePath(), "local.properties").exists() + && System.getenv("ANDROID_HOME") == null) { + return "You have to specify the Android SDK path or set the ANDROID_HOME environtment variable."; + } + + boolean isAPK = androidType.getText().equals(ANDROID_TYPES[0]); + + String task = "android:assembleFullRelease"; + File pkgFile = new File(Ctx.project.getProjectDir().getAbsolutePath(), + "android/build/outputs/apk/full/release/android-full-release.apk"); + + File destPkgFile = new File(dir.getText(), projectName + "-" + version.getText() + ".apk"); + + boolean genExpansion = Boolean.parseBoolean(expansionFile.getText()); - List params = new ArrayList(); + if (!isAPK) { // .aab + task = "android:bundleFullRelease"; + pkgFile = new File(Ctx.project.getProjectDir().getAbsolutePath(), + "android/build/outputs/bundle/fullRelease/android-full-release.aab"); + destPkgFile = new File(dir.getText(), projectName + "-" + version.getText() + ".aab"); + genExpansion = false; + } + + boolean newProjectStructure = new File(Ctx.project.getProjectDir().getAbsolutePath() + "/assets/").exists(); - if (iosSignIdentity.getText() != null) - params.add("-Probovm.iosSignIdentity=" + iosSignIdentity.getText()); + if (!newProjectStructure && genExpansion) + return "You need to update your project to the new layout to generate expansion files."; - if (iosProvisioningProfile.getText() != null) - params.add("-Probovm.iosProvisioningProfile=" + iosProvisioningProfile.getText()); - - if (customBuildParameters.getText() != null) - params.add(customBuildParams); + if (!newProjectStructure) { + task = "android:assembleRelease"; + pkgFile = new File(Ctx.project.getProjectDir().getAbsolutePath(), + "android/build/outputs/apk/android-release.apk"); + } + + if (genExpansion) { + task = "android:assembleExpansionRelease"; + pkgFile = new File(Ctx.project.getProjectDir().getAbsolutePath(), + "android/build/outputs/apk/expansion/release/android-expansion-release.apk"); + } - // Add clean target in IOS because the app. is not signing well if not cleaning. - params.add("ios:clean"); + if (!RunProccess.runGradle(Ctx.project.getProjectDir(), params + task)) { + return "Error Generating package"; + } - params.add("ios:createIPA"); + FileUtils.copyFile(pkgFile, destPkgFile); - if (RunProccess.runGradle(Ctx.project.getProjectDir(), params)) { + if (genExpansion) { + File fExp = findObb(Ctx.project.getProjectDir().getAbsolutePath() + "/android/build/distributions/", + versionCode.getText()); + FileUtils.copyFile(fExp, new File(dir.getText(), fExp.getName())); + } - String apk = Ctx.project.getProjectDir().getAbsolutePath() + "/ios/build/robovm/IOSLauncher.ipa"; + return null; + } - File f = new File(apk); - FileUtils.copyFile(f, new File(dir.getText(), projectName + "-" + version.getText() + ".ipa")); - } else { - msg = "Error Generating package"; + private String createDesktop(String projectName, String versionParam, String customBuildParams) throws IOException { + String jarDir = Ctx.project.getProjectDir().getAbsolutePath() + "/desktop/build/libs/"; + String jarName = projectName + "-desktop-" + version.getText() + ".jar"; + + String error = genDesktopJar(projectName, versionParam, jarDir, jarName, customBuildParams); + + if (error != null) + return error; + + if (desktopType.getText().equals(DESKTOP_TYPES[0])) { // BUNDLE JRE + String launcher = getDesktopMainClass(); + + if (os.getText().equals("linux64")) { + packr(Platform.Linux64, linux64JRE.getText(), projectName, jarDir + jarName, launcher, dir.getText()); + } else if (os.getText().equals("linux32")) { + packr(Platform.Linux32, linux32JRE.getText(), projectName, jarDir + jarName, launcher, dir.getText()); + } else if (os.getText().equals("windows32")) { + packr(Platform.Windows32, winJRE.getText(), projectName, jarDir + jarName, launcher, dir.getText()); + } else if (os.getText().equals("windows64")) { + packr(Platform.Windows64, winJRE64.getText(), projectName, jarDir + jarName, launcher, dir.getText()); + } else if (os.getText().equals("macOSX")) { + packr(Platform.MacOS, osxJRE.getText(), projectName, jarDir + jarName, launcher, dir.getText()); + } else if (os.getText().equals("all")) { + packr(Platform.Linux64, linux64JRE.getText(), projectName, jarDir + jarName, launcher, dir.getText()); + packr(Platform.Linux32, linux32JRE.getText(), projectName, jarDir + jarName, launcher, dir.getText()); + packr(Platform.Windows32, winJRE.getText(), projectName, jarDir + jarName, launcher, dir.getText()); + packr(Platform.Windows64, winJRE64.getText(), projectName, jarDir + jarName, launcher, dir.getText()); + packr(Platform.MacOS, osxJRE.getText(), projectName, jarDir + jarName, launcher, dir.getText()); } } - return msg; + return null; } private void archChanged() { @@ -439,9 +516,10 @@ private void archChanged() { String a = arch.getText(); if (a.equals("desktop")) { - setVisible(type, true); - typeChanged(); + setVisible(desktopType, true); + desktopTypeChanged(); } else if (a.equals("android")) { + setVisible(androidType, true); setVisible(versionCode, true); setVisible(androidSDK, true); setVisible(expansionFile, true); @@ -454,12 +532,12 @@ private void archChanged() { setVisible(iosSignIdentity, true); setVisible(iosProvisioningProfile, true); } - + setVisible(customBuildParameters, true); } - private void typeChanged() { - if (type.getText().equals(TYPES[0])) { + private void desktopTypeChanged() { + if (desktopType.getText().equals(DESKTOP_TYPES[0])) { setVisible(os, true); } else { setVisible(os, false); @@ -469,6 +547,14 @@ private void typeChanged() { osChanged(); } + private void androidTypeChanged() { + if (androidType.getText().equals(ANDROID_TYPES[0])) { + setVisible(expansionFile, true); + } else { + setVisible(expansionFile, false); + } + } + private void osChanged() { setVisible(icon, false); @@ -498,7 +584,7 @@ private void osChanged() { setVisible(linux64JRE, false); } - if (os.isVisible() && (os.getText().equals("macOSX") || os.getText().equals("all"))) { + if (os.isVisible() && (os.getText().equals("macOS") || os.getText().equals("all"))) { setVisible(osxJRE, true); setVisible(icon, true); } else { @@ -562,8 +648,8 @@ protected boolean validateFields() { return ok; } - private String genDesktopJar(String projectName, String versionParam, String jarDir, String jarName, String customBuildParams) - throws IOException { + private String genDesktopJar(String projectName, String versionParam, String jarDir, String jarName, + String customBuildParams) throws IOException { String msg = null; if (RunProccess.runGradle(Ctx.project.getProjectDir(), versionParam + customBuildParams + "desktop:dist")) { @@ -670,15 +756,14 @@ private void setCurrentVersion(String version) { * @return Search the desktop main class in the desktop folder */ private String getDesktopMainClass() { - - + File result = search(new File(Ctx.project.getProjectDir().getAbsolutePath() + "/desktop")); String absolutePath = result.getAbsolutePath().replace('\\', '/'); - int cutIdx = absolutePath.indexOf("src/main/java/"); - - if(cutIdx != -1) + int cutIdx = absolutePath.indexOf("src/main/java/"); + + if (cutIdx != -1) cutIdx += 14; else cutIdx = absolutePath.indexOf("src/") + 4; diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/ProjectPanel.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/ProjectPanel.java index 2f37df997..40076b66d 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/ProjectPanel.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/ProjectPanel.java @@ -24,24 +24,23 @@ import com.bladecoder.engineeditor.ui.panels.HeaderPanel; import com.bladecoder.engineeditor.ui.panels.TabPanel; -public class ProjectPanel extends HeaderPanel { +public class ProjectPanel extends HeaderPanel { private TabPanel tabPanel; private SceneList sceneList; private ChapterList chapterList; private SoundList soundList; - - + public ProjectPanel(Skin skin) { - super(skin, "ADVENTURE"); - + super(skin, "ADVENTURE"); + tabPanel = new TabPanel(skin); sceneList = new SceneList(skin); chapterList = new ChapterList(skin); soundList = new SoundList(skin); - + setContent(tabPanel); - + tabPanel.addTab("Scenes", sceneList); tabPanel.addTab("Chapters", chapterList); tabPanel.addTab("Game Props", new WorldProps(skin)); @@ -49,13 +48,14 @@ public ProjectPanel(Skin skin) { tabPanel.addTab("Assets", new AssetsList(skin)); tabPanel.addTab("Resolutions", new ResolutionList(skin)); - Ctx.project.addPropertyChangeListener(Project.NOTIFY_PROJECT_LOADED, new PropertyChangeListener() { @Override - public void propertyChange(PropertyChangeEvent e) { + public void propertyChange(PropertyChangeEvent e) { chapterList.addElements(); - setTile("ADV - " + (Ctx.project.getTitle() != null? Ctx.project.getTitle():"")); + setTile(Ctx.project.getTitle() != null + ? Ctx.project.getTitle() + " (" + Ctx.project.getWorld().getScenes().size() + " scenes)" + : "ADVENTURE GAME"); } - }); + }); } } diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/SceneList.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/SceneList.java index bcfafb5e3..423fc3ebc 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/SceneList.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/SceneList.java @@ -63,6 +63,7 @@ public class SceneList extends ModelList { private ImageButton initBtn; + private ImageButton reloadBtn; private SelectBox chapters; private HashMap bgIconCache = new HashMap<>(); private boolean disposeBgCache = false; @@ -105,6 +106,11 @@ public void clicked(InputEvent event, float x, float y) { initBtn.setDisabled(true); + reloadBtn = new ImageButton(skin); + toolbar.addToolBarButton(reloadBtn, "ic_reload_small", "Reload Assets", "Reload current scene assets"); + + reloadBtn.setDisabled(true); + list.addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { @@ -120,6 +126,7 @@ public void changed(ChangeEvent event, Actor actor) { toolbar.disableEdit(pos == -1); initBtn.setDisabled(pos == -1); + reloadBtn.setDisabled(pos == -1); } }); @@ -133,6 +140,14 @@ public void changed(ChangeEvent event, Actor actor) { }); + reloadBtn.addListener(new ChangeListener() { + @Override + public void changed(ChangeEvent event, Actor actor) { + reloadAssets(); + } + + }); + chapters.addListener(chapterListener); chapters.getSelection().setProgrammaticChangeEvents(false); @@ -260,6 +275,10 @@ private void setDefault() { Ctx.project.setModified(); } + private void reloadAssets() { + Ctx.project.setSelectedScene(list.getSelected()); + } + @Override protected void delete() { Scene s = removeSelected(); diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/SpriteList.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/SpriteList.java index c4aeeebe2..dc0796119 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/SpriteList.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/SpriteList.java @@ -32,6 +32,7 @@ import com.bladecoder.engine.model.SpriteActor; import com.bladecoder.engineeditor.Ctx; import com.bladecoder.engineeditor.common.ElementUtils; +import com.bladecoder.engineeditor.common.Message; import com.bladecoder.engineeditor.model.Project; import com.bladecoder.engineeditor.ui.panels.CellRenderer; import com.bladecoder.engineeditor.ui.panels.EditModelDialog; @@ -65,7 +66,7 @@ public void changed(ChangeEvent event, Actor actor) { String id = null; if (pos != -1) - id = list.getItems().get(pos).id; + id = list.getItems().get(pos).id; Ctx.project.setSelectedFA(id); @@ -88,12 +89,14 @@ public void changed(ChangeEvent event, Actor actor) { flipInit(); } }); - + Ctx.project.addPropertyChangeListener(Project.NOTIFY_ELEMENT_CREATED, new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { - if (evt.getNewValue() instanceof Dialog && !(evt.getSource() instanceof EditDialogDialog) && parent instanceof SpriteActor) { - HashMap animations = ((AnimationRenderer)parent.getRenderer()).getAnimations(); + if (evt.getNewValue() instanceof Dialog && !(evt.getSource() instanceof EditDialogDialog) + && parent instanceof SpriteActor) { + HashMap animations = ((AnimationRenderer) parent.getRenderer()) + .getAnimations(); addElements(parent, Arrays.asList(animations.values().toArray(new AnimationDesc[0]))); } } @@ -105,14 +108,14 @@ private void setDefault() { if (pos == -1) return; - - AnimationRenderer renderer = (AnimationRenderer)((SpriteActor) Ctx.project.getSelectedActor()).getRenderer(); + + AnimationRenderer renderer = (AnimationRenderer) ((SpriteActor) Ctx.project.getSelectedActor()).getRenderer(); String id = list.getItems().get(pos).id; String oldId = renderer.getInitAnimation(); - + renderer.setInitAnimation(id); - + Ctx.project.setModified(this, "init_animation", oldId, id); } @@ -121,46 +124,46 @@ private void flipInit() { if (pos == -1) return; - - AnimationRenderer renderer = (AnimationRenderer)((SpriteActor) Ctx.project.getSelectedActor()).getRenderer(); + + AnimationRenderer renderer = (AnimationRenderer) ((SpriteActor) Ctx.project.getSelectedActor()).getRenderer(); String id = list.getItems().get(pos).id; String newValue = AnimationRenderer.getFlipId(id); renderer.setInitAnimation(newValue); - + Ctx.project.setModified(this, "init_animation", id, newValue); } @Override protected void delete() { AnimationDesc d = removeSelected(); - - AnimationRenderer renderer = (AnimationRenderer)parent.getRenderer(); - + + AnimationRenderer renderer = (AnimationRenderer) parent.getRenderer(); + renderer.getAnimations().remove(d.id); - - // UNDO + + // UNDO Ctx.project.getUndoStack().add(new UndoDeleteAnimation(parent, d)); // delete init_animation attr if the animation to delete is the chapter // init_animation - if (renderer.getInitAnimation().equals(d.id)) { + if (d.id.equals(renderer.getInitAnimation())) { HashMap animations = renderer.getAnimations(); String newValue = null; - if(animations.size() > 0) + if (animations.size() > 0) newValue = animations.keySet().iterator().next(); - + renderer.setInitAnimation(newValue); - + Ctx.project.setModified(this, "init_animation", d.id, newValue); } - + Ctx.project.setModified(); } - + @Override protected void copy() { AnimationDesc e = list.getSelected(); @@ -168,29 +171,35 @@ protected void copy() { if (e == null) return; - clipboard = (AnimationDesc)ElementUtils.cloneElement(e); + clipboard = (AnimationDesc) ElementUtils.cloneElement(e); toolbar.disablePaste(false); } @Override protected void paste() { - AnimationDesc newElement = (AnimationDesc)ElementUtils.cloneElement(clipboard); - + AnimationDesc newElement = (AnimationDesc) ElementUtils.cloneElement(clipboard); + + if (((AnimationRenderer) parent.getRenderer()).getAnimations().get(newElement.id) != null) { + Message.showMsg(getStage(), "Animation already exists.", 2); + + return; + } + int pos = list.getSelectedIndex() + 1; list.getItems().insert(pos, newElement); - ((AnimationRenderer)parent.getRenderer()).addAnimation(newElement); + ((AnimationRenderer) parent.getRenderer()).addAnimation(newElement); list.setSelectedIndex(pos); list.invalidateHierarchy(); - + Ctx.project.setModified(); - } + } @Override protected EditModelDialog getEditElementDialogInstance(AnimationDesc e) { - return new EditAnimationDialog(skin, parent, e); + return new EditAnimationDialog(skin, parent, e); } // ------------------------------------------------------------------------- @@ -202,7 +211,7 @@ protected EditModelDialog getEditElementDialogInstan protected String getCellTitle(AnimationDesc e) { String name = e.id; SpriteActor actor = (SpriteActor) Ctx.project.getSelectedActor(); - AnimationRenderer renderer = (AnimationRenderer)actor.getRenderer(); + AnimationRenderer renderer = (AnimationRenderer) actor.getRenderer(); String init = renderer.getInitAnimation(); diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/VerbList.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/VerbList.java index 5c92bc954..c3893020e 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/VerbList.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/VerbList.java @@ -57,23 +57,19 @@ public VerbList(Skin skin) { @Override public void scopeChanged(String scope) { if (WORLD_SCOPE.equals(scope)) { - addElements(Ctx.project.getWorld().getVerbManager(), Arrays.asList(Ctx.project.getWorld() - .getVerbManager().getVerbs().values().toArray(new Verb[0]))); + addElements(Ctx.project.getWorld().getVerbManager(), Arrays + .asList(Ctx.project.getWorld().getVerbManager().getVerbs().values().toArray(new Verb[0]))); } else if (SCENE_SCOPE.equals(scope)) { if (Ctx.project.getSelectedScene() != null) - addElements( - Ctx.project.getSelectedScene().getVerbManager(), - Arrays.asList(Ctx.project.getSelectedScene().getVerbManager().getVerbs().values() - .toArray(new Verb[0]))); + addElements(Ctx.project.getSelectedScene().getVerbManager(), Arrays.asList(Ctx.project + .getSelectedScene().getVerbManager().getVerbs().values().toArray(new Verb[0]))); else addElements(null, null); } else if (ACTOR_SCOPE.equals(scope)) { BaseActor a = Ctx.project.getSelectedActor(); if (a instanceof InteractiveActor) { - addElements( - ((InteractiveActor) a).getVerbManager(), - Arrays.asList(((InteractiveActor) a).getVerbManager().getVerbs().values() - .toArray(new Verb[0]))); + addElements(((InteractiveActor) a).getVerbManager(), Arrays.asList( + ((InteractiveActor) a).getVerbManager().getVerbs().values().toArray(new Verb[0]))); } else { addElements(null, null); } @@ -109,22 +105,16 @@ public void changed(ChangeEvent event, Actor actor) { public void propertyChange(PropertyChangeEvent evt) { if (evt.getNewValue() instanceof Verb && !(evt.getSource() instanceof EditVerbDialog)) { if (ScopePanel.WORLD_SCOPE.equals(scopePanel.getScope())) { - addElements( - Ctx.project.getWorld().getVerbManager(), - Arrays.asList(Ctx.project.getWorld().getVerbManager().getVerbs().values() - .toArray(new Verb[0]))); + addElements(Ctx.project.getWorld().getVerbManager(), Arrays.asList( + Ctx.project.getWorld().getVerbManager().getVerbs().values().toArray(new Verb[0]))); } else if (ScopePanel.SCENE_SCOPE.equals(scopePanel.getScope())) { - addElements( - Ctx.project.getSelectedScene().getVerbManager(), - Arrays.asList(Ctx.project.getSelectedScene().getVerbManager().getVerbs().values() - .toArray(new Verb[0]))); + addElements(Ctx.project.getSelectedScene().getVerbManager(), Arrays.asList(Ctx.project + .getSelectedScene().getVerbManager().getVerbs().values().toArray(new Verb[0]))); } else if (ScopePanel.ACTOR_SCOPE.equals(scopePanel.getScope())) { BaseActor a = Ctx.project.getSelectedActor(); if (a instanceof InteractiveActor) { - addElements( - ((InteractiveActor) a).getVerbManager(), - Arrays.asList(((InteractiveActor) a).getVerbManager().getVerbs().values() - .toArray(new Verb[0]))); + addElements(((InteractiveActor) a).getVerbManager(), Arrays.asList( + ((InteractiveActor) a).getVerbManager().getVerbs().values().toArray(new Verb[0]))); } else { addElements(null, null); } @@ -254,13 +244,16 @@ protected String getCellSubTitle(Verb e) { String state = e.getState(); String target = e.getTarget(); - StringBuilder sb = new StringBuilder(e.getId()); + StringBuilder sb = new StringBuilder(); if (state != null) - sb.append(" when ").append(state); + sb.append("when ").append(state); if (target != null) - sb.append(" with target '").append(target).append("'"); + sb.append(" target: '").append(target).append("'"); + + if (e.getIcon() != null) + sb.append(" icon: '").append(e.getIcon()).append("'"); return sb.toString(); } diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/WorldProps.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/WorldProps.java index 7a11335ce..213d3f20a 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/WorldProps.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/WorldProps.java @@ -54,7 +54,7 @@ protected void updateModel(String property, String value) { } else if (property.equals(Config.SINGLE_ACTION_INVENTORY)) { Ctx.project.getProjectConfig().setProperty(Config.SINGLE_ACTION_INVENTORY, value); } else if (property.equals(Config.FAST_LEAVE)) { - Ctx.project.getProjectConfig().setProperty(Config.FAST_LEAVE, value); + Ctx.project.getProjectConfig().setProperty(Config.FAST_LEAVE, value); } else if (property.equals(Config.DEBUG_PROP)) { Ctx.project.getProjectConfig().setProperty(Config.DEBUG_PROP, value); } else if (property.equals(Config.CHARACTER_ICON_ATLAS)) { @@ -65,6 +65,8 @@ protected void updateModel(String property, String value) { Ctx.project.getProjectConfig().setProperty(Config.AUTO_HIDE_TEXTS, value); } else if (property.equals(Config.EXTEND_VIEWPORT_PROP)) { Ctx.project.getProjectConfig().setProperty(Config.EXTEND_VIEWPORT_PROP, value); + } else if (property.equals(Config.SHOW_HOTSPOTS)) { + Ctx.project.getProjectConfig().setProperty(Config.SHOW_HOTSPOTS, value); } Ctx.project.setModified(); // TODO Add propertychange to Config @@ -77,15 +79,18 @@ private void setProject() { addProperty(Project.WIDTH_PROPERTY, Ctx.project.getWorld().getWidth()); addProperty(Project.HEIGHT_PROPERTY, Ctx.project.getWorld().getHeight()); addProperty(Config.TITLE_PROP, Ctx.project.getTitle()); - addProperty(Config.INVENTORY_POS_PROP, - Ctx.project.getProjectConfig().getProperty(Config.INVENTORY_POS_PROP, "DOWN").toUpperCase(Locale.ENGLISH), new String[] {"TOP", "DOWN", "LEFT", "RIGHT", "CENTER"}); + addProperty( + Config.INVENTORY_POS_PROP, Ctx.project.getProjectConfig() + .getProperty(Config.INVENTORY_POS_PROP, "DOWN").toUpperCase(Locale.ENGLISH), + new String[] { "TOP", "DOWN", "LEFT", "RIGHT", "CENTER" }); addProperty(Config.INVENTORY_AUTOSIZE_PROP, Boolean .parseBoolean(Ctx.project.getProjectConfig().getProperty(Config.INVENTORY_AUTOSIZE_PROP, "true"))); - addProperty(Config.UI_MODE, Ctx.project.getProjectConfig().getProperty(Config.UI_MODE, "TWO_BUTTONS").toUpperCase(Locale.ENGLISH), new String[] {"TWO_BUTTONS", "PIE", "SINGLE_CLICK"}); + addProperty(Config.UI_MODE, Ctx.project.getProjectConfig().getProperty(Config.UI_MODE, "TWO_BUTTONS") + .toUpperCase(Locale.ENGLISH), new String[] { "TWO_BUTTONS", "PIE", "SINGLE_CLICK" }); addProperty(Config.SINGLE_ACTION_INVENTORY, Boolean .parseBoolean(Ctx.project.getProjectConfig().getProperty(Config.SINGLE_ACTION_INVENTORY, "false"))); - addProperty(Config.FAST_LEAVE, Boolean - .parseBoolean(Ctx.project.getProjectConfig().getProperty(Config.FAST_LEAVE, "false"))); + addProperty(Config.FAST_LEAVE, + Boolean.parseBoolean(Ctx.project.getProjectConfig().getProperty(Config.FAST_LEAVE, "false"))); addProperty(Config.DEBUG_PROP, Boolean.parseBoolean(Ctx.project.getProjectConfig().getProperty(Config.DEBUG_PROP, "false"))); addProperty(Config.SHOW_DESC_PROP, @@ -95,7 +100,9 @@ private void setProject() { addProperty(Config.CHARACTER_ICON_ATLAS, Ctx.project.getProjectConfig().getProperty(Config.CHARACTER_ICON_ATLAS, "")); addProperty(Config.EXTEND_VIEWPORT_PROP, Boolean - .parseBoolean(Ctx.project.getProjectConfig().getProperty(Config.EXTEND_VIEWPORT_PROP, "true"))); + .parseBoolean(Ctx.project.getProjectConfig().getProperty(Config.EXTEND_VIEWPORT_PROP, "false"))); + addProperty(Config.SHOW_HOTSPOTS, + Boolean.parseBoolean(Ctx.project.getProjectConfig().getProperty(Config.SHOW_HOTSPOTS, "true"))); } invalidateHierarchy(); diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/panels/EditDialog.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/panels/EditDialog.java index f8250be97..7b6afc57a 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/panels/EditDialog.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/panels/EditDialog.java @@ -50,16 +50,17 @@ public EditDialog(String title, Skin skin) { infoLbl = new Label("", skin); infoLbl.setWrap(true); centerPanel = new Table(skin); - infoCell = getContentTable().add((Widget) infoLbl).prefWidth(200); - getContentTable().add(new ScrollPane(centerPanel, skin)) - .maxHeight(Gdx.graphics.getHeight() * 0.8f) - .maxWidth(Gdx.graphics.getWidth() * 0.7f) - .minHeight(Gdx.graphics.getHeight() * 0.5f) + infoCell = getContentTable().add((Widget) infoLbl).prefWidth(200).height(Gdx.graphics.getHeight() * 0.5f); + getContentTable().add(new ScrollPane(centerPanel, skin)).maxHeight(Gdx.graphics.getHeight() * 0.8f) + .maxWidth(Gdx.graphics.getWidth() * 0.7f).minHeight(Gdx.graphics.getHeight() * 0.5f) .minWidth(Gdx.graphics.getWidth() * 0.5f); - + + getContentTable().setHeight(Gdx.graphics.getHeight() * 0.7f); + centerPanel.addListener(new InputListener() { @Override - public void enter(InputEvent event, float x, float y, int pointer, com.badlogic.gdx.scenes.scene2d.Actor fromActor) { + public void enter(InputEvent event, float x, float y, int pointer, + com.badlogic.gdx.scenes.scene2d.Actor fromActor) { // EditorLogger.debug("ENTER - X: " + x + " Y: " + y); getStage().setScrollFocus(centerPanel); } @@ -75,25 +76,26 @@ public void enter(InputEvent event, float x, float y, int pointer, com.badlogic padLeft(10); padRight(10); } - + public void addInputPanel(InputPanel i) { getCenterPanel().row().fill().expandX(); getCenterPanel().add(i); } - + public void setVisible(InputPanel i, boolean v) { i.setVisible(v); - Cell c = getCenterPanel().getCell(i); - - if(v) { + Cell c = getCenterPanel().getCell(i); + + if (v) { c.height(i.getPrefHeight()); } else { c.height(1); } - + i.invalidateHierarchy(); - } + } + @Override public Skin getSkin() { return skin; } diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/panels/EditableSelectBox.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/panels/EditableSelectBox.java index b160b8ee1..1ec8f51ac 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/panels/EditableSelectBox.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/panels/EditableSelectBox.java @@ -66,6 +66,7 @@ public EditableSelectBox(Skin skin) { add(showListButton); addListener(new ClickListener() { + @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { if (pointer == 0 && button != 0) return false; @@ -74,11 +75,12 @@ public boolean touchDown(InputEvent event, float x, float y, int pointer, int bu return false; // if (selectList.getStage() == null) - showList(); + showList(); return true; } + @Override public boolean keyUp(InputEvent event, int keycode) { if (keycode == Keys.ENTER) { setSelectedIndex(selectList.list.getSelectedIndex()); @@ -102,7 +104,7 @@ public boolean keyUp(InputEvent event, int keycode) { if (selectList.getStage() == null && selectList.list.getItems().size > 0) { showList(); } - + filterItems(input.getText()); } @@ -177,10 +179,10 @@ private final void setListItems(T[] newItems) { public void showList() { if (selectList.list.getItems().size == 0) return; - - if(selectList.list.getSelectedIndex() >= selectList.list.getItems().size) + + if (selectList.list.getSelectedIndex() >= selectList.list.getItems().size) selectList.list.setSelectedIndex(selectList.list.getItems().size - 1); - + selectList.show(getStage()); } @@ -196,7 +198,7 @@ private void filterItems(String s) { setListItems(items); } else { - ArrayList filtered = new ArrayList(); + ArrayList filtered = new ArrayList<>(); String sl = s.toLowerCase(); @@ -241,12 +243,14 @@ public SelectList(Skin skin, final TextField inputBox) { setActor(list); list.addListener(new ClickListener() { + @Override public void clicked(InputEvent event, float x, float y) { selectBox.setText(list.getSelected().toString()); selectedIndex = list.getSelectedIndex(); hide(); } + @Override public boolean mouseMoved(InputEvent event, float x, float y) { list.setSelectedIndex( Math.min(list.getItems().size - 1, (int) ((list.getHeight() - y) / list.getItemHeight()))); @@ -255,9 +259,10 @@ public boolean mouseMoved(InputEvent event, float x, float y) { }); addListener(new InputListener() { + @Override public void exit(InputEvent event, float x, float y, int pointer, Actor toActor) { if (toActor == null || !isAscendantOf(toActor)) - if(selectedIndex < list.getItems().size) + if (selectedIndex < list.getItems().size) list.setSelectedIndex(selectedIndex); else EditorLogger.error("EditableSelectBox:exit selectedIndex outOfBounds: " + selectedIndex); @@ -265,15 +270,22 @@ public void exit(InputEvent event, float x, float y, int pointer, Actor toActor) }); hideListener = new InputListener() { + @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { Actor target = event.getTarget(); if (isAscendantOf(target)) return false; - list.setSelectedIndex(selectedIndex); + + if (selectedIndex < list.getItems().size) + list.setSelectedIndex(selectedIndex); + else + EditorLogger.error("EditableSelectBox:touchDown selectedIndex outOfBounds: " + selectedIndex); + hide(); return false; } + @Override public boolean keyDown(InputEvent event, int keycode) { if (keycode == Keys.ESCAPE) hide(); @@ -332,7 +344,6 @@ public void show(Stage stage) { previousScrollFocus = actor; stage.setScrollFocus(this); - list.setTouchable(Touchable.enabled); clearActions(); // getColor().a = 0; @@ -359,6 +370,7 @@ public void hide() { addAction(sequence(fadeOut(0.15f, Interpolation.fade), Actions.removeActor())); } + @Override public void draw(Batch batch, float parentAlpha) { selectBox.localToStageCoordinates(temp.set(0, 0)); if (!temp.equals(screenPosition)) @@ -366,6 +378,7 @@ public void draw(Batch batch, float parentAlpha) { super.draw(batch, parentAlpha); } + @Override public void act(float delta) { super.act(delta); toFront(); diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/panels/FilteredSelectBox.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/panels/FilteredSelectBox.java index bbd5dcbb8..e8d45dea0 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/panels/FilteredSelectBox.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/panels/FilteredSelectBox.java @@ -39,585 +39,667 @@ import com.badlogic.gdx.utils.Pool; import com.badlogic.gdx.utils.Pools; - -/** A select box (aka a drop-down list) allows a user to choose one of a number of values from a list. When inactive, the selected - * value is displayed. When activated, it shows the list of values that may be selected. +/** + * A select box (aka a drop-down list) allows a user to choose one of a number + * of values from a list. When inactive, the selected value is displayed. When + * activated, it shows the list of values that may be selected. *

* {@link ChangeEvent} is fired when the selectbox selection changes. *

- * The preferred size of the select box is determined by the maximum text bounds of the items and the size of the - * {@link FilteredSelectBoxStyle#background}. + * The preferred size of the select box is determined by the maximum text bounds + * of the items and the size of the {@link FilteredSelectBoxStyle#background}. + * * @author mzechner * @author Nathan Sweet * @author Rafael García */ public class FilteredSelectBox extends Widget implements Disableable { - - static final Vector2 temp = new Vector2(); - - FilteredSelectBoxStyle style; - final Array items = new Array(); - final ArraySelection selection = new ArraySelection(items); - SelectBoxList selectBoxList; - private float prefWidth, prefHeight; - private ClickListener clickListener; - boolean disabled; - private int alignment = Align.left; - - public FilteredSelectBox (Skin skin) { - this(skin.get(FilteredSelectBoxStyle.class)); - } - - public FilteredSelectBox (Skin skin, String styleName) { - this(skin.get(styleName, FilteredSelectBoxStyle.class)); - } - - public FilteredSelectBox (FilteredSelectBoxStyle style) { - setStyle(style); - setSize(getPrefWidth(), getPrefHeight()); - - selection.setActor(this); - selection.setRequired(true); - - selectBoxList = new SelectBoxList(this); - - addListener(clickListener = new ClickListener() { - public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) { - if (pointer == 0 && button != 0) return false; - if (disabled) return false; - if (selectBoxList.hasParent()) - hideList(); - else - showList(); - return true; - } - }); - - } - - /** Set the max number of items to display when the select box is opened. Set to 0 (the default) to display as many as fit in - * the stage height. */ - public void setMaxListCount (int maxListCount) { - selectBoxList.maxListCount = maxListCount; - } - /** @return Max number of items to display when the box is opened, or <= 0 to display them all. */ - public int getMaxListCount () { - return selectBoxList.maxListCount; - } - - protected void setStage (Stage stage) { - if (stage == null) selectBoxList.hide(); - super.setStage(stage); - } - - public void setStyle (FilteredSelectBoxStyle style) { - if (style == null) throw new IllegalArgumentException("style cannot be null."); - this.style = style; - if (selectBoxList != null) { - selectBoxList.setStyle(style.scrollStyle); - selectBoxList.list.setStyle(style.listStyle); + static final Vector2 temp = new Vector2(); + + FilteredSelectBoxStyle style; + final Array items = new Array<>(); + final ArraySelection selection = new ArraySelection<>(items); + SelectBoxList selectBoxList; + private float prefWidth, prefHeight; + private ClickListener clickListener; + boolean disabled; + private int alignment = Align.left; + + public FilteredSelectBox(Skin skin) { + this(skin.get(FilteredSelectBoxStyle.class)); + } + + public FilteredSelectBox(Skin skin, String styleName) { + this(skin.get(styleName, FilteredSelectBoxStyle.class)); + } + + public FilteredSelectBox(FilteredSelectBoxStyle style) { + setStyle(style); + setSize(getPrefWidth(), getPrefHeight()); + + selection.setActor(this); + selection.setRequired(true); + + selectBoxList = new SelectBoxList<>(this); + + addListener(clickListener = new ClickListener() { + @Override + public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { + if (pointer == 0 && button != 0) + return false; + if (disabled) + return false; + if (selectBoxList.hasParent()) + hideList(); + else + showList(); + return true; } + }); + + } + + /** + * Set the max number of items to display when the select box is opened. Set to + * 0 (the default) to display as many as fit in the stage height. + */ + public void setMaxListCount(int maxListCount) { + selectBoxList.maxListCount = maxListCount; + } + + /** + * @return Max number of items to display when the box is opened, or <= 0 to + * display them all. + */ + public int getMaxListCount() { + return selectBoxList.maxListCount; + } + + @Override + protected void setStage(Stage stage) { + if (stage == null) + selectBoxList.hide(); + super.setStage(stage); + } + + public void setStyle(FilteredSelectBoxStyle style) { + if (style == null) + throw new IllegalArgumentException("style cannot be null."); + this.style = style; + if (selectBoxList != null) { + selectBoxList.setStyle(style.scrollStyle); + selectBoxList.list.setStyle(style.listStyle); + } + invalidateHierarchy(); + } + + /** + * Returns the select box's style. Modifying the returned style may not have an + * effect until {@link #setStyle(FilteredSelectBoxStyle)} is called. + */ + public FilteredSelectBoxStyle getStyle() { + return style; + } + + /** + * Set the backing Array that makes up the choices available in the SelectBox + */ + @SuppressWarnings("unchecked") + public void setItems(T... newItems) { + if (newItems == null) + throw new IllegalArgumentException("newItems cannot be null."); + float oldPrefWidth = getPrefWidth(); + + items.clear(); + items.addAll(newItems); + selection.validate(); + selectBoxList.list.setItems(items); + + invalidate(); + if (oldPrefWidth != getPrefWidth()) invalidateHierarchy(); - } - - /** Returns the select box's style. Modifying the returned style may not have an effect until {@link #setStyle(FilteredSelectBoxStyle)} - * is called. */ - public FilteredSelectBoxStyle getStyle () { - return style; - } - - /** Set the backing Array that makes up the choices available in the SelectBox */ - @SuppressWarnings("unchecked") - public void setItems (T... newItems) { - if (newItems == null) throw new IllegalArgumentException("newItems cannot be null."); - float oldPrefWidth = getPrefWidth(); + } - items.clear(); - items.addAll(newItems); - selection.validate(); - selectBoxList.list.setItems(items); + /** Sets the items visible in the select box. */ + public void setItems(Array newItems) { + if (newItems == null) + throw new IllegalArgumentException("newItems cannot be null."); + float oldPrefWidth = getPrefWidth(); - invalidate(); - if (oldPrefWidth != getPrefWidth()) invalidateHierarchy(); - } + items.clear(); + items.addAll(newItems); + selection.validate(); + selectBoxList.list.setItems(items); - /** Sets the items visible in the select box. */ - public void setItems (Array newItems) { - if (newItems == null) throw new IllegalArgumentException("newItems cannot be null."); - float oldPrefWidth = getPrefWidth(); - - items.clear(); - items.addAll(newItems); - selection.validate(); - selectBoxList.list.setItems(items); - - invalidate(); - if (oldPrefWidth != getPrefWidth()) invalidateHierarchy(); - } - - public void clearItems () { - if (items.size == 0) return; - items.clear(); - selection.clear(); + invalidate(); + if (oldPrefWidth != getPrefWidth()) invalidateHierarchy(); - } - - /** Returns the internal items array. If modified, {@link #setItems(Array)} must be called to reflect the changes. */ - public Array getItems () { - return items; - } - - @Override - public void layout () { - Drawable bg = style.background; - BitmapFont font = style.font; - - if (bg != null) { - prefHeight = Math.max(bg.getTopHeight() + bg.getBottomHeight() + font.getCapHeight() - font.getDescent() * 2, + } + + public void clearItems() { + if (items.size == 0) + return; + items.clear(); + selection.clear(); + invalidateHierarchy(); + } + + /** + * Returns the internal items array. If modified, {@link #setItems(Array)} must + * be called to reflect the changes. + */ + public Array getItems() { + return items; + } + + @Override + public void layout() { + Drawable bg = style.background; + BitmapFont font = style.font; + + if (bg != null) { + prefHeight = Math.max( + bg.getTopHeight() + bg.getBottomHeight() + font.getCapHeight() - font.getDescent() * 2, bg.getMinHeight()); - } else - prefHeight = font.getCapHeight() - font.getDescent() * 2; - - float maxItemWidth = 0; - Pool layoutPool = Pools.get(GlyphLayout.class); - GlyphLayout layout = layoutPool.obtain(); - for (int i = 0; i < items.size; i++) { - layout.setText(font, toString(items.get(i))); - maxItemWidth = Math.max(layout.width, maxItemWidth); - } - layoutPool.free(layout); - - prefWidth = maxItemWidth; - if (bg != null) prefWidth += bg.getLeftWidth() + bg.getRightWidth(); - - ListStyle listStyle = style.listStyle; - ScrollPaneStyle scrollStyle = style.scrollStyle; - float listWidth = maxItemWidth + listStyle.selection.getLeftWidth() + listStyle.selection.getRightWidth(); - if (scrollStyle.background != null) - listWidth += scrollStyle.background.getLeftWidth() + scrollStyle.background.getRightWidth(); - if (selectBoxList == null || !selectBoxList.isScrollingDisabledY()) - listWidth += Math.max(style.scrollStyle.vScroll != null ? style.scrollStyle.vScroll.getMinWidth() : 0, + } else + prefHeight = font.getCapHeight() - font.getDescent() * 2; + + float maxItemWidth = 0; + Pool layoutPool = Pools.get(GlyphLayout.class); + GlyphLayout layout = layoutPool.obtain(); + for (int i = 0; i < items.size; i++) { + layout.setText(font, toString(items.get(i))); + maxItemWidth = Math.max(layout.width, maxItemWidth); + } + layoutPool.free(layout); + + prefWidth = maxItemWidth; + if (bg != null) + prefWidth += bg.getLeftWidth() + bg.getRightWidth(); + + ListStyle listStyle = style.listStyle; + ScrollPaneStyle scrollStyle = style.scrollStyle; + float listWidth = maxItemWidth + listStyle.selection.getLeftWidth() + listStyle.selection.getRightWidth(); + if (scrollStyle.background != null) + listWidth += scrollStyle.background.getLeftWidth() + scrollStyle.background.getRightWidth(); + if (selectBoxList == null || !selectBoxList.isScrollingDisabledY()) + listWidth += Math.max(style.scrollStyle.vScroll != null ? style.scrollStyle.vScroll.getMinWidth() : 0, style.scrollStyle.vScrollKnob != null ? style.scrollStyle.vScrollKnob.getMinWidth() : 0); - prefWidth = Math.max(prefWidth, listWidth); - } - - @Override - public void draw (Batch batch, float parentAlpha) { - validate(); - - Drawable background; - if (disabled && style.backgroundDisabled != null) - background = style.backgroundDisabled; - else if (selectBoxList.hasParent() && style.backgroundOpen != null) - background = style.backgroundOpen; - else if (clickListener.isOver() && style.backgroundOver != null) - background = style.backgroundOver; - else if (style.background != null) - background = style.background; - else - background = null; - BitmapFont font = style.font; - Color fontColor = (disabled && style.disabledFontColor != null) ? style.disabledFontColor : style.fontColor; - - Color color = getColor(); - float x = getX(), y = getY(); - float width = getWidth(), height = getHeight(); - - batch.setColor(color.r, color.g, color.b, color.a * parentAlpha); - if (background != null) background.draw(batch, x, y, width, height); - - T selected = selection.first(); - if (selected != null) { - if (background != null) { - width -= background.getLeftWidth() + background.getRightWidth(); - height -= background.getBottomHeight() + background.getTopHeight(); - x += background.getLeftWidth(); - y += (int)(height / 2 + background.getBottomHeight() + font.getData().capHeight / 2); - } else { - y += (int)(height / 2 + font.getData().capHeight / 2); - } - font.setColor(fontColor.r, fontColor.g, fontColor.b, fontColor.a * parentAlpha); - drawItem(batch, font, selected, x, y, width); + prefWidth = Math.max(prefWidth, listWidth); + } + + @Override + public void draw(Batch batch, float parentAlpha) { + validate(); + + Drawable background; + if (disabled && style.backgroundDisabled != null) + background = style.backgroundDisabled; + else if (selectBoxList.hasParent() && style.backgroundOpen != null) + background = style.backgroundOpen; + else if (clickListener.isOver() && style.backgroundOver != null) + background = style.backgroundOver; + else if (style.background != null) + background = style.background; + else + background = null; + BitmapFont font = style.font; + Color fontColor = (disabled && style.disabledFontColor != null) ? style.disabledFontColor : style.fontColor; + + Color color = getColor(); + float x = getX(), y = getY(); + float width = getWidth(), height = getHeight(); + + batch.setColor(color.r, color.g, color.b, color.a * parentAlpha); + if (background != null) + background.draw(batch, x, y, width, height); + + T selected = selection.first(); + if (selected != null) { + if (background != null) { + width -= background.getLeftWidth() + background.getRightWidth(); + height -= background.getBottomHeight() + background.getTopHeight(); + x += background.getLeftWidth(); + y += (int) (height / 2 + background.getBottomHeight() + font.getData().capHeight / 2); + } else { + y += (int) (height / 2 + font.getData().capHeight / 2); } - } - - protected GlyphLayout drawItem (Batch batch, BitmapFont font, T item, float x, float y, float width) { - String string = toString(item); - return font.draw(batch, string, x, y, 0, string.length(), width, alignment, false, "..."); - } - - /** Sets the alignment of the selected item in the select box. See {@link #getList()} and {@link List#setAlignment(int)} to set - * the alignment in the list shown when the select box is open. - * @param alignment See {@link Align}. */ - public void setAlignment (int alignment) { - this.alignment = alignment; - } - - /** Get the set of selected items, useful when multiple items are selected - * @return a Selection object containing the selected elements */ - public ArraySelection getSelection () { - return selection; - } - - /** Returns the first selected item, or null. For multiple selections use {@link SelectBox#getSelection()}. */ - public T getSelected () { - return selection.first(); - } - - /** Sets the selection to only the passed item, if it is a possible choice, else selects the first item. */ - public void setSelected (T item) { + font.setColor(fontColor.r, fontColor.g, fontColor.b, fontColor.a * parentAlpha); + drawItem(batch, font, selected, x, y, width); + } + } + + protected GlyphLayout drawItem(Batch batch, BitmapFont font, T item, float x, float y, float width) { + String string = toString(item); + return font.draw(batch, string, x, y, 0, string.length(), width, alignment, false, "..."); + } + + /** + * Sets the alignment of the selected item in the select box. See + * {@link #getList()} and {@link List#setAlignment(int)} to set the alignment in + * the list shown when the select box is open. + * + * @param alignment See {@link Align}. + */ + public void setAlignment(int alignment) { + this.alignment = alignment; + } + + /** + * Get the set of selected items, useful when multiple items are selected + * + * @return a Selection object containing the selected elements + */ + public ArraySelection getSelection() { + return selection; + } + + /** + * Returns the first selected item, or null. For multiple selections use + * {@link SelectBox#getSelection()}. + */ + public T getSelected() { + return selection.first(); + } + + /** + * Sets the selection to only the passed item, if it is a possible choice, else + * selects the first item. + */ + public void setSelected(T item) { // T item = (T)i; - if (items.contains(item, false)) - selection.set(item); - else if (items.size > 0) - selection.set(items.first()); - else - selection.clear(); - } - - /** @return The index of the first selected item. The top item has an index of 0. Nothing selected has an index of -1. */ - public int getSelectedIndex () { - ObjectSet selected = selection.items(); - return selected.size == 0 ? -1 : items.indexOf(selected.first(), false); - } - - /** Sets the selection to only the selected index. */ - public void setSelectedIndex (int index) { - selection.set(items.get(index)); - } - - public void setDisabled (boolean disabled) { - if (disabled && !this.disabled) hideList(); - this.disabled = disabled; - } - - public boolean isDisabled () { - return disabled; - } - - public float getPrefWidth () { - validate(); - return prefWidth; - } - - public float getPrefHeight () { - validate(); - return prefHeight; - } + if (items.contains(item, false)) + selection.set(item); + else if (items.size > 0) + selection.set(items.first()); + else + selection.clear(); + } + + /** + * @return The index of the first selected item. The top item has an index of 0. + * Nothing selected has an index of -1. + */ + public int getSelectedIndex() { + ObjectSet selected = selection.items(); + return selected.size == 0 ? -1 : items.indexOf(selected.first(), false); + } + + /** Sets the selection to only the selected index. */ + public void setSelectedIndex(int index) { + selection.set(items.get(index)); + } + + @Override + public void setDisabled(boolean disabled) { + if (disabled && !this.disabled) + hideList(); + this.disabled = disabled; + } + + @Override + public boolean isDisabled() { + return disabled; + } + + @Override + public float getPrefWidth() { + validate(); + return prefWidth; + } + + @Override + public float getPrefHeight() { + validate(); + return prefHeight; + } + + protected String toString(T item) { + return item.toString(); + } + + public void showList() { + if (items.size == 0) + return; + selectBoxList.show(getStage()); + } + + public void hideList() { + selectBoxList.hide(); + } + + /** Returns the list shown when the select box is open. */ + public List getList() { + return selectBoxList.list; + } + + /** Disables scrolling of the list shown when the select box is open. */ + public void setScrollingDisabled(boolean y) { + selectBoxList.setScrollingDisabled(true, y); + invalidateHierarchy(); + } + + /** + * Returns the scroll pane containing the list that is shown when the select box + * is open. + */ + public ScrollPane getScrollPane() { + return selectBoxList; + } + + protected void onShow(Actor selectBoxList, boolean below) { + selectBoxList.getColor().a = 0; + selectBoxList.addAction(fadeIn(0.3f, Interpolation.fade)); + } + + protected void onHide(Actor selectBoxList) { + selectBoxList.getColor().a = 1; + selectBoxList.addAction(sequence(fadeOut(0.15f, Interpolation.fade), removeActor())); + } + + /** @author Nathan Sweet */ + final class SelectBoxList extends ScrollPane { + private final FilteredSelectBox selectBox; + int maxListCount; + private final Vector2 screenPosition = new Vector2(); + final List list; + private InputListener hideListener; + private Actor previousScrollFocus; + private TextField filterField; + + public SelectBoxList(final FilteredSelectBox selectBox) { + super(null, selectBox.style.scrollStyle); + this.selectBox = selectBox; + + setOverscroll(false, false); + setFadeScrollBars(false); + setScrollingDisabled(true, false); + + list = new List(selectBox.style.listStyle) { + @Override + public String toString(T obj) { + return selectBox.toString(obj); + } + }; - protected String toString (T item) { - return item.toString(); - } + list.setTouchable(Touchable.disabled); + setActor(list); - public void showList () { - if (items.size == 0) return; - selectBoxList.show(getStage()); - } + filterField = new TextField("", selectBox.style.textFieldStyle); - public void hideList () { - selectBoxList.hide(); - } + list.addListener(new ClickListener() { + @Override + public void clicked(InputEvent event, float x, float y) { + selectBox.selection.choose(list.getSelected()); + hide(); + } - /** Returns the list shown when the select box is open. */ - public List getList () { - return selectBoxList.list; - } + @Override + public boolean mouseMoved(InputEvent event, float x, float y) { + list.setSelectedIndex( + Math.min(list.getItems().size - 1, (int) ((list.getHeight() - y) / list.getItemHeight()))); + return true; + } + }); - /** Disables scrolling of the list shown when the select box is open. */ - public void setScrollingDisabled (boolean y) { - selectBoxList.setScrollingDisabled(true, y); - invalidateHierarchy(); - } + addListener(new InputListener() { + @Override + public void exit(InputEvent event, float x, float y, int pointer, Actor toActor) { + if (toActor == null || !isAscendantOf(toActor)) + list.getSelection().set(selectBox.getSelected()); + } + }); - /** Returns the scroll pane containing the list that is shown when the select box is open. */ - public ScrollPane getScrollPane () { - return selectBoxList; - } + hideListener = new InputListener() { + @Override + public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { + Actor target = event.getTarget(); + if (isAscendantOf(target)) + return false; + list.getSelection().set(selectBox.getSelected()); + hide(); + return false; + } - protected void onShow (Actor selectBoxList, boolean below) { - selectBoxList.getColor().a = 0; - selectBoxList.addAction(fadeIn(0.3f, Interpolation.fade)); - } + @Override + public boolean keyDown(InputEvent event, int keycode) { + if (keycode == Keys.ESCAPE) + hide(); + return false; + } + }; - protected void onHide (Actor selectBoxList) { - selectBoxList.getColor().a = 1; - selectBoxList.addAction(sequence(fadeOut(0.15f, Interpolation.fade), removeActor())); - } + filterField.addListener(new InputListener() { + @Override + public boolean keyUp(InputEvent event, int keycode) { + if (keycode == Keys.ENTER) { + setSelected(list.getSelected()); + hideList(); + filterField.setCursorPosition(filterField.getText().length()); + } else if (keycode == Keys.UP) { + int idx = list.getSelectedIndex(); - /** @author Nathan Sweet */ - final class SelectBoxList extends ScrollPane { - private final FilteredSelectBox selectBox; - int maxListCount; - private final Vector2 screenPosition = new Vector2(); - final List list; - private InputListener hideListener; - private Actor previousScrollFocus; - private TextField filterField; - - public SelectBoxList (final FilteredSelectBox selectBox) { - super(null, selectBox.style.scrollStyle); - this.selectBox = selectBox; - - setOverscroll(false, false); - setFadeScrollBars(false); - setScrollingDisabled(true, false); - - list = new List(selectBox.style.listStyle) { - @Override - protected String toString (T obj) { - return selectBox.toString(obj); - } - }; - list.setTouchable(Touchable.disabled); - setActor(list); - - filterField = new TextField("", selectBox.style.textFieldStyle); - - list.addListener(new ClickListener() { - public void clicked (InputEvent event, float x, float y) { - selectBox.selection.choose(list.getSelected()); - hide(); - } + if (idx > 0) + list.setSelectedIndex(idx - 1); - public boolean mouseMoved (InputEvent event, float x, float y) { - list.setSelectedIndex(Math.min(list.getItems().size - 1, (int)((list.getHeight() - y) / list.getItemHeight()))); return true; - } - }); + } else if (keycode == Keys.DOWN) { + int idx = list.getSelectedIndex(); - addListener(new InputListener() { - public void exit (InputEvent event, float x, float y, int pointer, Actor toActor) { - if (toActor == null || !isAscendantOf(toActor)) list.getSelection().set(selectBox.getSelected()); - } - }); + if (idx < list.getItems().size - 1) + list.setSelectedIndex(idx + 1); - hideListener = new InputListener() { - public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) { - Actor target = event.getTarget(); - if (isAscendantOf(target)) return false; - list.getSelection().set(selectBox.getSelected()); - hide(); - return false; - } - - public boolean keyDown (InputEvent event, int keycode) { - if (keycode == Keys.ESCAPE) hide(); - return false; - } - }; - - filterField.addListener(new InputListener() { - public boolean keyUp(InputEvent event, int keycode) { - if (keycode == Keys.ENTER) { - setSelected(list.getSelected()); - hideList(); - filterField.setCursorPosition(filterField.getText().length()); - } else if (keycode == Keys.UP) { - int idx = list.getSelectedIndex(); - - if (idx > 0) - list.setSelectedIndex(idx - 1); - - return true; - } else if (keycode == Keys.DOWN) { - int idx = list.getSelectedIndex(); - - if (idx < list.getItems().size - 1) - list.setSelectedIndex(idx + 1); - - return true; - } else { - if (getStage() == null && list.getItems().size > 0) { - showList(); - } - - filterItems(filterField.getText()); + return true; + } else { + if (getStage() == null && list.getItems().size > 0) { + showList(); } - return false; + filterItems(filterField.getText()); } - }); - } - - @SuppressWarnings("unchecked") - private void filterItems(String s) { - if (s == null || s.isEmpty()) { - setListItems((T[])items.toArray()); - } else { + return false; + } + }); + } - ArrayList filtered = new ArrayList(); + @SuppressWarnings("unchecked") + private void filterItems(String s) { - String sl = s.toLowerCase(); + if (s == null || s.isEmpty()) { + setListItems(items.toArray()); + } else { - for (T item : (T[])items.toArray()) { - if (item.toString().toLowerCase().contains(sl)) - filtered.add(item); - } + ArrayList filtered = new ArrayList<>(); - setListItems((T[]) filtered.toArray(new String[filtered.size()])); + String sl = s.toLowerCase(); + + for (T item : items.toArray()) { + if (item.toString().toLowerCase().contains(sl)) + filtered.add(item); } -// hideList(); - invalidate(); -// showList(); + setListItems((T[]) filtered.toArray(new String[filtered.size()])); } - - private final void setListItems(T[] newItems) { - if (newItems == null) - throw new IllegalArgumentException("newItems cannot be null."); - float oldPrefWidth = getPrefWidth(); - - list.setItems(newItems); - if (newItems.length > 0) - list.setSelectedIndex(0); - else - list.setSelectedIndex(-1); +// hideList(); + invalidate(); +// showList(); + } - invalidate(); - if (oldPrefWidth != getPrefWidth()) - invalidateHierarchy(); - } + private final void setListItems(T[] newItems) { + if (newItems == null) + throw new IllegalArgumentException("newItems cannot be null."); - public void show (Stage stage) { - if (list.isTouchable()) return; + float oldPrefWidth = getPrefWidth(); - stage.removeCaptureListener(hideListener); - stage.addCaptureListener(hideListener); - stage.addActor(this); - stage.addActor(filterField); - - selectBox.localToStageCoordinates(screenPosition.set(0, 0)); - - // Show the list above or below the select box, limited to a number of items and the available height in the stage. - float itemHeight = list.getItemHeight(); - float height = itemHeight * (maxListCount <= 0 ? selectBox.items.size : Math.min(maxListCount, selectBox.items.size)); - Drawable scrollPaneBackground = getStyle().background; - if (scrollPaneBackground != null) height += scrollPaneBackground.getTopHeight() + scrollPaneBackground.getBottomHeight(); - Drawable listBackground = list.getStyle().background; - if (listBackground != null) height += listBackground.getTopHeight() + listBackground.getBottomHeight(); - - float heightBelow = screenPosition.y - itemHeight; - float heightAbove = stage.getCamera().viewportHeight - screenPosition.y - selectBox.getHeight(); - boolean below = true; - if (height > heightBelow) { - if (heightAbove > heightBelow) { - below = false; - height = Math.min(height, heightAbove); - } else - height = heightBelow; - } + list.setItems(newItems); + if (newItems.length > 0) + list.setSelectedIndex(0); + else + list.setSelectedIndex(-1); - if (below) - setY(screenPosition.y - height); - else - setY(screenPosition.y + selectBox.getHeight()); - setX(screenPosition.x); - setHeight(height); - validate(); - float width = Math.max(getPrefWidth(), selectBox.getWidth()); - if (getPrefHeight() > height && !isScrollingDisabledY()) width += getScrollBarWidth(); - setWidth(width); - - filterField.setX(getX()); - filterField.setWidth(getWidth()); - filterField.setHeight(filterField.getPrefHeight()); - filterField.setY(getY() + getHeight() - filterField.getHeight()); - stage.setKeyboardFocus(filterField); - filterField.validate(); - setY(getY() - filterField.getHeight()); - - validate(); - scrollTo(0, list.getHeight() - selectBox.getSelectedIndex() * itemHeight - itemHeight / 2, 0, 0, true, true); - updateVisualScroll(); - - previousScrollFocus = null; - Actor actor = stage.getScrollFocus(); - if (actor != null && !actor.isDescendantOf(this)) previousScrollFocus = actor; - stage.setScrollFocus(this); - - list.getSelection().set(selectBox.getSelected()); - list.setTouchable(Touchable.enabled); - clearActions(); - selectBox.onShow(this, below); - - filterField.setText(""); - setListItems((T[])items.toArray()); + invalidate(); + if (oldPrefWidth != getPrefWidth()) + invalidateHierarchy(); + } + + public void show(Stage stage) { + if (list.isTouchable()) + return; + + stage.removeCaptureListener(hideListener); + stage.addCaptureListener(hideListener); + stage.addActor(this); + stage.addActor(filterField); + + selectBox.localToStageCoordinates(screenPosition.set(0, 0)); + + // Show the list above or below the select box, limited to a number of items and + // the available height in the stage. + float itemHeight = list.getItemHeight(); + float height = itemHeight + * (maxListCount <= 0 ? selectBox.items.size : Math.min(maxListCount, selectBox.items.size)); + Drawable scrollPaneBackground = getStyle().background; + if (scrollPaneBackground != null) + height += scrollPaneBackground.getTopHeight() + scrollPaneBackground.getBottomHeight(); + Drawable listBackground = list.getStyle().background; + if (listBackground != null) + height += listBackground.getTopHeight() + listBackground.getBottomHeight(); + + float heightBelow = screenPosition.y - itemHeight; + float heightAbove = stage.getCamera().viewportHeight - screenPosition.y - selectBox.getHeight(); + boolean below = true; + if (height > heightBelow) { + if (heightAbove > heightBelow) { + below = false; + height = Math.min(height, heightAbove); + } else + height = heightBelow; } - public void hide () { - if (!list.isTouchable() || !hasParent()) return; - list.setTouchable(Touchable.disabled); + if (below) + setY(screenPosition.y - height); + else + setY(screenPosition.y + selectBox.getHeight()); + setX(screenPosition.x); + setHeight(height); + validate(); + float width = Math.max(getPrefWidth(), selectBox.getWidth()); + if (getPrefHeight() > height && !isScrollingDisabledY()) + width += getScrollBarWidth(); + setWidth(width); + + filterField.setX(getX()); + filterField.setWidth(getWidth()); + filterField.setHeight(filterField.getPrefHeight()); + filterField.setY(getY() + getHeight() - filterField.getHeight()); + stage.setKeyboardFocus(filterField); + filterField.validate(); + setY(getY() - filterField.getHeight()); - Stage stage = getStage(); - if (stage != null) { - stage.removeCaptureListener(hideListener); - if (previousScrollFocus != null && previousScrollFocus.getStage() == null) previousScrollFocus = null; - Actor actor = stage.getScrollFocus(); - if (actor == null || isAscendantOf(actor)) stage.setScrollFocus(previousScrollFocus); - } + validate(); + scrollTo(0, list.getHeight() - selectBox.getSelectedIndex() * itemHeight - itemHeight / 2, 0, 0, true, + true); + updateVisualScroll(); - clearActions(); - selectBox.onHide(this); - filterField.remove(); - } + previousScrollFocus = null; + Actor actor = stage.getScrollFocus(); + if (actor != null && !actor.isDescendantOf(this)) + previousScrollFocus = actor; + stage.setScrollFocus(this); - public void draw (Batch batch, float parentAlpha) { - selectBox.localToStageCoordinates(temp.set(0, 0)); - if (!temp.equals(screenPosition)) hide(); - super.draw(batch, parentAlpha); - } + list.getSelection().set(selectBox.getSelected()); + list.setTouchable(Touchable.enabled); + clearActions(); + selectBox.onShow(this, below); - public void act (float delta) { - super.act(delta); - toFront(); - filterField.toFront(); - } + filterField.setText(""); + setListItems(items.toArray()); } - /** The style for a select box, see {@link SelectBox}. - * @author mzechner - * @author Nathan Sweet */ - static public class FilteredSelectBoxStyle { - public BitmapFont font; - public Color fontColor = new Color(1, 1, 1, 1); - /** Optional. */ - public Color disabledFontColor; - /** Optional. */ - public Drawable background; - public ScrollPaneStyle scrollStyle; - public ListStyle listStyle; - public TextFieldStyle textFieldStyle; - /** Optional. */ - public Drawable backgroundOver, backgroundOpen, backgroundDisabled; - - public FilteredSelectBoxStyle () { - } + public void hide() { + if (!list.isTouchable() || !hasParent()) + return; + list.setTouchable(Touchable.disabled); - public FilteredSelectBoxStyle (BitmapFont font, Color fontColor, Drawable background, ScrollPaneStyle scrollStyle, - ListStyle listStyle, TextFieldStyle textFieldStyle) { - this.font = font; - this.fontColor.set(fontColor); - this.background = background; - this.scrollStyle = scrollStyle; - this.listStyle = listStyle; - this.textFieldStyle = textFieldStyle; + Stage stage = getStage(); + if (stage != null) { + stage.removeCaptureListener(hideListener); + if (previousScrollFocus != null && previousScrollFocus.getStage() == null) + previousScrollFocus = null; + Actor actor = stage.getScrollFocus(); + if (actor == null || isAscendantOf(actor)) + stage.setScrollFocus(previousScrollFocus); } - public FilteredSelectBoxStyle (FilteredSelectBoxStyle style) { - this.font = style.font; - this.fontColor.set(style.fontColor); - if (style.disabledFontColor != null) this.disabledFontColor = new Color(style.disabledFontColor); - this.background = style.background; - this.backgroundOver = style.backgroundOver; - this.backgroundOpen = style.backgroundOpen; - this.backgroundDisabled = style.backgroundDisabled; - this.scrollStyle = new ScrollPaneStyle(style.scrollStyle); - this.listStyle = new ListStyle(style.listStyle); - this.textFieldStyle = new TextFieldStyle(style.textFieldStyle); - } + clearActions(); + selectBox.onHide(this); + filterField.remove(); } + @Override + public void draw(Batch batch, float parentAlpha) { + selectBox.localToStageCoordinates(temp.set(0, 0)); + if (!temp.equals(screenPosition)) + hide(); + super.draw(batch, parentAlpha); + } + + @Override + public void act(float delta) { + super.act(delta); + toFront(); + filterField.toFront(); + } + } + + /** + * The style for a select box, see {@link SelectBox}. + * + * @author mzechner + * @author Nathan Sweet + */ + static public class FilteredSelectBoxStyle { + public BitmapFont font; + public Color fontColor = new Color(1, 1, 1, 1); + /** Optional. */ + public Color disabledFontColor; + /** Optional. */ + public Drawable background; + public ScrollPaneStyle scrollStyle; + public ListStyle listStyle; + public TextFieldStyle textFieldStyle; + /** Optional. */ + public Drawable backgroundOver, backgroundOpen, backgroundDisabled; + + public FilteredSelectBoxStyle() { + } + + public FilteredSelectBoxStyle(BitmapFont font, Color fontColor, Drawable background, + ScrollPaneStyle scrollStyle, ListStyle listStyle, TextFieldStyle textFieldStyle) { + this.font = font; + this.fontColor.set(fontColor); + this.background = background; + this.scrollStyle = scrollStyle; + this.listStyle = listStyle; + this.textFieldStyle = textFieldStyle; + } + + public FilteredSelectBoxStyle(FilteredSelectBoxStyle style) { + this.font = style.font; + this.fontColor.set(style.fontColor); + if (style.disabledFontColor != null) + this.disabledFontColor = new Color(style.disabledFontColor); + this.background = style.background; + this.backgroundOver = style.backgroundOver; + this.backgroundOpen = style.backgroundOpen; + this.backgroundDisabled = style.backgroundDisabled; + this.scrollStyle = new ScrollPaneStyle(style.scrollStyle); + this.listStyle = new ListStyle(style.listStyle); + this.textFieldStyle = new TextFieldStyle(style.textFieldStyle); + } + } + } diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/panels/ModelList.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/panels/ModelList.java index d6de12848..6830c434d 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/panels/ModelList.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/panels/ModelList.java @@ -61,7 +61,7 @@ public void addElements(PARENT parent, List elements) { if (sorted) { list.sortByTitle(); } - + if (getItems().size > 0) list.getSelection().choose(list.getItems().get(0)); @@ -81,8 +81,11 @@ protected void create() { @Override public void changed(ChangeEvent event, Actor actor) { T e = ((EditModelDialog) actor).getElement(); - addItem(e); - + + if (getItems().indexOf(e, true) == -1) { + addItem(e); + } + if (sorted) { list.sortByTitle(); } diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/undo/UndoDeleteVerb.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/undo/UndoDeleteVerb.java index e184c4c48..253eac9da 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/undo/UndoDeleteVerb.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/undo/UndoDeleteVerb.java @@ -5,15 +5,15 @@ import com.bladecoder.engineeditor.Ctx; import com.bladecoder.engineeditor.model.Project; - public class UndoDeleteVerb implements UndoOp { private VerbManager vm; private Verb v; - + public UndoDeleteVerb(VerbManager vm, Verb v) { this.vm = vm; + this.v = v; } - + @Override public void undo() { vm.addVerb(v); diff --git a/adventure-editor/src/main/resources/images/icons.atlas b/adventure-editor/src/main/resources/images/icons.atlas index 17818dd80..deeb27f7f 100644 --- a/adventure-editor/src/main/resources/images/icons.atlas +++ b/adventure-editor/src/main/resources/images/icons.atlas @@ -153,7 +153,7 @@ ic_create_all index: -1 ic_create_all_disabled rotate: false - xy: 861, 51 + xy: 913, 51 size: 15, 15 orig: 15, 15 offset: 0, 0 @@ -424,6 +424,20 @@ ic_quit orig: 45, 45 offset: 0, 0 index: -1 +ic_reload_small + rotate: false + xy: 651, 42 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ic_reload_small_disabled + rotate: false + xy: 677, 42 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 ic_repeat rotate: false xy: 326, 47 @@ -433,14 +447,14 @@ ic_repeat index: -1 ic_right rotate: false - xy: 651, 42 + xy: 703, 42 size: 24, 24 orig: 24, 24 offset: 0, 0 index: -1 ic_right_disabled rotate: false - xy: 677, 42 + xy: 729, 42 size: 24, 24 orig: 24, 24 offset: 0, 0 @@ -489,14 +503,14 @@ ic_text index: -1 ic_up rotate: false - xy: 703, 42 + xy: 755, 42 size: 24, 24 orig: 24, 24 offset: 0, 0 index: -1 ic_up_disabled rotate: false - xy: 729, 42 + xy: 781, 42 size: 24, 24 orig: 24, 24 offset: 0, 0 @@ -538,21 +552,21 @@ scn_move index: -1 scn_rotate rotate: false - xy: 755, 42 + xy: 807, 42 size: 24, 24 orig: 24, 24 offset: 0, 0 index: -1 scn_scale rotate: false - xy: 805, 48 + xy: 857, 48 size: 18, 18 orig: 18, 18 offset: 0, 0 index: -1 scn_scale_lock rotate: false - xy: 781, 44 + xy: 833, 44 size: 22, 22 orig: 22, 22 offset: 0, 0 @@ -573,14 +587,14 @@ title index: -1 transparent-dark rotate: false - xy: 825, 50 + xy: 877, 50 size: 16, 16 orig: 16, 16 offset: 0, 0 index: -1 transparent-light rotate: false - xy: 843, 50 + xy: 895, 50 size: 16, 16 orig: 16, 16 offset: 0, 0 diff --git a/adventure-editor/src/main/resources/images/icons.png b/adventure-editor/src/main/resources/images/icons.png index 4705d447e..6aa6ac77d 100644 Binary files a/adventure-editor/src/main/resources/images/icons.png and b/adventure-editor/src/main/resources/images/icons.png differ diff --git a/adventure-editor/src/main/resources/projectTmpl/android/AndroidManifest.xml b/adventure-editor/src/main/resources/projectTmpl/android/AndroidManifest.xml index 51bc1f818..35d8642fe 100644 --- a/adventure-editor/src/main/resources/projectTmpl/android/AndroidManifest.xml +++ b/adventure-editor/src/main/resources/projectTmpl/android/AndroidManifest.xml @@ -1,10 +1,12 @@ + + diff --git a/adventure-editor/src/main/resources/projectTmpl/android/build.gradle b/adventure-editor/src/main/resources/projectTmpl/android/build.gradle index e74a5fea7..7332a9b6d 100644 --- a/adventure-editor/src/main/resources/projectTmpl/android/build.gradle +++ b/adventure-editor/src/main/resources/projectTmpl/android/build.gradle @@ -5,7 +5,7 @@ android { defaultConfig { applicationId "%PACKAGE%" - minSdkVersion 15 + minSdkVersion 21 targetSdkVersion '%API_LEVEL%' versionName version @@ -86,7 +86,7 @@ android { configurations { natives } dependencies { - api project(":core") + implementation project(":core") api "com.badlogicgames.gdx:gdx-backend-android:$gdxVersion" natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi" natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi-v7a" @@ -101,6 +101,8 @@ dependencies { // TO QUERY PERMISSIONS implementation "com.android.support:support-v4:23.0.0" + + implementation "com.badlogicgames.gdx-controllers:gdx-controllers-android:$gdxControllersVersion" } // called every time gradle gets executed, takes the native dependencies of @@ -130,6 +132,12 @@ task copyAndroidNatives() { } } +tasks.whenTaskAdded { packageTask -> + if (packageTask.name.contains("package")) { + packageTask.dependsOn 'copyAndroidNatives' + } +} + task run(type: Exec) { def path def localProperties = project.file("../local.properties") @@ -149,7 +157,7 @@ task run(type: Exec) { } def adb = path + "/platform-tools/adb" - commandLine "$adb", 'shell', 'am', 'start', '-n', android.defaultConfig.applicationId + '/' + android.defaultConfig.applicationId + '.AndroidLauncher' + commandLine "$adb", 'shell', 'am', 'start', '-n', android.defaultConfig.applicationId + '/' + 'com.bladecoder.engine.AndroidLauncher' } task('packageMainExpansionFile', type: Zip) { @@ -175,61 +183,6 @@ tasks.whenTaskAdded { task -> } } -// sets up the Android Eclipse project, using the old Ant based build. -eclipse { - // need to specify Java source sets explicitely, SpringSource Gradle Eclipse plugin - // ignores any nodes added in classpath.file.withXml - sourceSets { - main { - java.srcDirs "src/main/java", 'gen' - } - } - - jdt { - sourceCompatibility=1.7 - targetCompatibility=1.7 - } - - classpath { - plusConfigurations += [ project.configurations.compile ] - containers 'com.android.ide.eclipse.adt.ANDROID_FRAMEWORK', 'com.android.ide.eclipse.adt.LIBRARIES' - } - - project { - name = appName + "-android" - natures 'com.android.ide.eclipse.adt.AndroidNature' - buildCommands.clear(); - buildCommand "com.android.ide.eclipse.adt.ResourceManagerBuilder" - buildCommand "com.android.ide.eclipse.adt.PreCompilerBuilder" - buildCommand "org.eclipse.jdt.core.javabuilder" - buildCommand "com.android.ide.eclipse.adt.ApkBuilder" - } -} - -// sets up the Android Idea project, using the old Ant based build. -idea { - module { - sourceDirs += file("src"); - scopes = [ COMPILE: [plus:[project.configurations.compile]]] - - iml { - withXml { - def node = it.asNode() - def builder = NodeBuilder.newInstance(); - builder.current = node; - builder.component(name: "FacetManager") { - facet(type: "android", name: "Android") { - configuration { - option(name: "UPDATE_PROPERTY_FILES", value:"true") - } - } - } - } - } - } -} - - String highestSdkAvailable(String defaultSdk) { try { def buildToolsDir = new File(android.getSdkDirectory().toString(), "platforms") diff --git a/adventure-editor/src/main/resources/projectTmpl/android/src/AndroidLauncher b/adventure-editor/src/main/resources/projectTmpl/android/src/AndroidLauncher index a954355d6..a34d4b4d9 100644 --- a/adventure-editor/src/main/resources/projectTmpl/android/src/AndroidLauncher +++ b/adventure-editor/src/main/resources/projectTmpl/android/src/AndroidLauncher @@ -1,4 +1,4 @@ -package %PACKAGE%; +package com.bladecoder.engine; import com.bladecoder.engine.BladeEngine; @@ -54,21 +54,20 @@ public class AndroidLauncher extends AndroidApplication { if (Build.VERSION.SDK_INT >= 23) { if (checkSelfPermission( Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { - Log.v(TAG, "Permission is granted but .obb was not found."); + Log.e(TAG, "Permission is granted but .obb was not found."); // TODO Download .obb exit(); } else { Log.v(TAG, "Permission is revoked, requesting..."); + bladeEngine.setWaitingForPermissions(true); ActivityCompat.requestPermissions(AndroidLauncher.this, new String[] { Manifest.permission.READ_EXTERNAL_STORAGE }, 2); } } else { // permission is automatically granted on sdk<23 upon installation - Log.v(TAG, "Permission is granted in Manifest (sdk<23) but .obb was not found."); + Log.e(TAG, "Permission is granted in Manifest (sdk<23) but .obb was not found."); // TODO Download .obb exit(); } - } else { - bladeEngine.createWithExpansion(); } } } @@ -85,15 +84,13 @@ public class AndroidLauncher extends AndroidApplication { boolean success = ((AndroidFiles) Gdx.files).setAPKExpansion(BuildConfig.EXPANSION_FILE_VERSION, 0); if (!success) { - Log.v(TAG, "Permission accepted but .obb was not found."); + Log.e(TAG, "Permission accepted but .obb was not found."); // TODO Download .obb exit(); - } else { - bladeEngine.createWithExpansion(); - } + } } else { - Log.v(TAG, "Permission denied by user."); + Log.e(TAG, "Permission denied by user."); exit(); } break; @@ -112,17 +109,40 @@ public class AndroidLauncher extends AndroidApplication { } class AndroidEngine extends BladeEngine { + private boolean waitingForPermissions = false; + @Override public void create() { - if (BuildConfig.EXPANSION_FILE_VERSION == 0) { - super.create(); + if(waitingForPermissions) + return; + + if (BuildConfig.EXPANSION_FILE_VERSION > 0) { + boolean success = ((AndroidFiles) Gdx.files).setAPKExpansion(BuildConfig.EXPANSION_FILE_VERSION, 0); + + if(!success) { + Log.e(TAG, "Can not set APK expansion."); + Gdx.app.exit(); + } else { + Log.d(TAG, "Expansion file stablish successfully!"); + } } + + super.create(); } - public void createWithExpansion() { - ((AndroidFiles) Gdx.files).setAPKExpansion(BuildConfig.EXPANSION_FILE_VERSION, 0); + @Override + public void resume() { + if(waitingForPermissions) { + // returns from querying permissions + bladeEngine.setWaitingForPermissions(false); + bladeEngine.create(); + } + + super.resume(); + } - super.create(); + public void setWaitingForPermissions(boolean v) { + waitingForPermissions = v; } } } \ No newline at end of file diff --git a/adventure-editor/src/main/resources/projectTmpl/assets/BladeEngine.properties b/adventure-editor/src/main/resources/projectTmpl/assets/BladeEngine.properties index 4aedc72d4..f7f3c56d4 100644 --- a/adventure-editor/src/main/resources/projectTmpl/assets/BladeEngine.properties +++ b/adventure-editor/src/main/resources/projectTmpl/assets/BladeEngine.properties @@ -2,7 +2,6 @@ title=Blade Engine Adventure ui_mode=two_buttons inventory_pos=down debug=false -#load_gamestate= -#play_recording=full -#force_res=1920 +resolutions=1 + diff --git a/adventure-editor/src/main/resources/projectTmpl/core/build.gradle b/adventure-editor/src/main/resources/projectTmpl/core/build.gradle index aac4e67c4..a804b2a23 100644 --- a/adventure-editor/src/main/resources/projectTmpl/core/build.gradle +++ b/adventure-editor/src/main/resources/projectTmpl/core/build.gradle @@ -5,7 +5,6 @@ sourceCompatibility = 1.7 targetCompatibility=1.7 [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' -eclipse.project.name = appName + '-core' dependencies { api "com.badlogicgames.gdx:gdx:$gdxVersion" @@ -14,6 +13,8 @@ dependencies { implementation "com.bladecoder.ink:blade-ink:$bladeInkVersion" implementation "org.minimalcode:minimalcode-beans:0.5.1" implementation "com.badlogicgames.gdx:gdx-freetype:$gdxVersion" + implementation "com.badlogicgames.gdx-controllers:gdx-controllers-core:$gdxControllersVersion" + if(project.hasProperty("spinePlugin") && project.spinePlugin.equals("true")) { println "Using Spine Plugin..." diff --git a/adventure-editor/src/main/resources/projectTmpl/desktop/build.gradle b/adventure-editor/src/main/resources/projectTmpl/desktop/build.gradle index 355ddeb35..98ee7855f 100644 --- a/adventure-editor/src/main/resources/projectTmpl/desktop/build.gradle +++ b/adventure-editor/src/main/resources/projectTmpl/desktop/build.gradle @@ -6,14 +6,14 @@ targetCompatibility=1.7 sourceSets.main.resources.srcDirs += [ rootProject.file('assets').absolutePath ] -mainClassName = "%PACKAGE%.desktop.DesktopLauncher" -eclipse.project.name = appName + '-desktop' +mainClassName = "com.bladecoder.engine.DesktopLauncher" dependencies { implementation project(":core") implementation "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion" implementation "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop" implementation "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop" + implementation "com.badlogicgames.gdx-controllers:gdx-controllers-desktop:$gdxControllersVersion" } run { diff --git a/adventure-editor/src/main/resources/projectTmpl/desktop/src/DesktopLauncher b/adventure-editor/src/main/resources/projectTmpl/desktop/src/DesktopLauncher index df8cf30d1..6abe6df01 100644 --- a/adventure-editor/src/main/resources/projectTmpl/desktop/src/DesktopLauncher +++ b/adventure-editor/src/main/resources/projectTmpl/desktop/src/DesktopLauncher @@ -1,4 +1,4 @@ -package %PACKAGE%.desktop; +package com.bladecoder.engine; import java.io.IOException; import java.io.InputStream; diff --git a/adventure-editor/src/main/resources/projectTmpl/gradle.properties b/adventure-editor/src/main/resources/projectTmpl/gradle.properties index 5294df70a..8b99e8296 100644 --- a/adventure-editor/src/main/resources/projectTmpl/gradle.properties +++ b/adventure-editor/src/main/resources/projectTmpl/gradle.properties @@ -9,6 +9,7 @@ androidGradlePluginVersion=%ANDROID_GRADLE_PLUGIN_VERSION% bladeInkVersion=%BLADE_INK_VERSION% spinePlugin=%USE_SPINE% +gdxControllersVersion=2.0.1 org.gradle.daemon=true org.gradle.jvmargs=-Xms128m -Xmx1536m diff --git a/adventure-editor/src/main/resources/projectTmpl/gradle/wrapper/gradle-wrapper.properties b/adventure-editor/src/main/resources/projectTmpl/gradle/wrapper/gradle-wrapper.properties index e0b3fb8d7..4d9ca1649 100644 --- a/adventure-editor/src/main/resources/projectTmpl/gradle/wrapper/gradle-wrapper.properties +++ b/adventure-editor/src/main/resources/projectTmpl/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/adventure-editor/src/main/resources/projectTmpl/ios/Info.plist.xml b/adventure-editor/src/main/resources/projectTmpl/ios/Info.plist.xml index 7b4e11581..773249514 100644 --- a/adventure-editor/src/main/resources/projectTmpl/ios/Info.plist.xml +++ b/adventure-editor/src/main/resources/projectTmpl/ios/Info.plist.xml @@ -5,7 +5,7 @@ UIRequiresFullScreen yes MinimumOSVersion - 7.0 + 8.0 CFBundleDevelopmentRegion en @@ -40,7 +40,6 @@ UIRequiredDeviceCapabilities - armv7 opengles-2 UISupportedInterfaceOrientations @@ -50,5 +49,15 @@ CFBundleIconName AppIcon + + GCSupportedGameControllers + + + ProfileName + ExtendedGamepad + + + GCSupportsControllerUserInteraction + diff --git a/adventure-editor/src/main/resources/projectTmpl/ios/build.gradle b/adventure-editor/src/main/resources/projectTmpl/ios/build.gradle index cb4421b7c..86fa8f6c7 100644 --- a/adventure-editor/src/main/resources/projectTmpl/ios/build.gradle +++ b/adventure-editor/src/main/resources/projectTmpl/ios/build.gradle @@ -6,7 +6,7 @@ sourceCompatibility = 1.7 targetCompatibility=1.7 ext { - mainClassName = "%PACKAGE%.IOSLauncher" + mainClassName = "com.bladecoder.engine.IOSLauncher" } launchIPhoneSimulator.dependsOn build @@ -30,4 +30,6 @@ dependencies { compile "com.badlogicgames.gdx:gdx-backend-robovm:$gdxVersion" compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-ios" compile "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-ios" + + implementation "com.badlogicgames.gdx-controllers:gdx-controllers-ios:$gdxControllersVersion" } diff --git a/adventure-editor/src/main/resources/projectTmpl/ios/robovm.properties b/adventure-editor/src/main/resources/projectTmpl/ios/robovm.properties index f0ccc54cd..05619e28e 100644 --- a/adventure-editor/src/main/resources/projectTmpl/ios/robovm.properties +++ b/adventure-editor/src/main/resources/projectTmpl/ios/robovm.properties @@ -1,6 +1,6 @@ app.version=1.0 app.id=%PACKAGE% -app.mainclass=%PACKAGE%.IOSLauncher +app.mainclass=com.bladecoder.engine.IOSLauncher app.executable=IOSLauncher app.build=1 app.name=%APP_NAME% diff --git a/adventure-editor/src/main/resources/projectTmpl/ios/robovm.xml b/adventure-editor/src/main/resources/projectTmpl/ios/robovm.xml index d729ad43c..2d5086b41 100644 --- a/adventure-editor/src/main/resources/projectTmpl/ios/robovm.xml +++ b/adventure-editor/src/main/resources/projectTmpl/ios/robovm.xml @@ -34,6 +34,7 @@ com.android.org.bouncycastle.crypto.digests.AndroidDigestFactoryOpenSSL org.apache.harmony.security.provider.cert.DRLCertFactory org.apache.harmony.security.provider.crypto.CryptoProvider + com.badlogic.gdx.controllers.IosControllerManager z @@ -46,6 +47,7 @@ OpenAL AudioToolbox AVFoundation + GameKit diff --git a/adventure-editor/src/main/resources/projectTmpl/ios/src/IOSLauncher b/adventure-editor/src/main/resources/projectTmpl/ios/src/IOSLauncher index 18e7dc684..11b966e44 100644 --- a/adventure-editor/src/main/resources/projectTmpl/ios/src/IOSLauncher +++ b/adventure-editor/src/main/resources/projectTmpl/ios/src/IOSLauncher @@ -1,4 +1,4 @@ -package %PACKAGE%; +package com.bladecoder.engine; import org.robovm.apple.foundation.NSAutoreleasePool; import org.robovm.apple.uikit.UIApplication; diff --git a/adventure-editor/src/main/resources/versions.properties b/adventure-editor/src/main/resources/versions.properties index da89e7592..a9474d91c 100644 --- a/adventure-editor/src/main/resources/versions.properties +++ b/adventure-editor/src/main/resources/versions.properties @@ -1,8 +1,7 @@ -androidAPILevel=27 -androidGradlePluginVersion=3.2.1 -bladeInkVersion=0.5.1 -buildToolsVersion=28.0.3 -libgdxVersion=1.9.9 -roboVMGradlePluginVersion=2.3.5 -roboVMVersion=2.3.5 -version=3.1.1-SNAPSHOT +androidAPILevel=30 +androidGradlePluginVersion=3.5.4 +bladeInkVersion=0.7.4 +libgdxVersion=1.9.13 +roboVMGradlePluginVersion=2.3.12 +roboVMVersion=2.3.12 +version=4.0.2 diff --git a/blade-engine-spine-plugin/build.gradle b/blade-engine-spine-plugin/build.gradle index 415323fa1..455cac023 100644 --- a/blade-engine-spine-plugin/build.gradle +++ b/blade-engine-spine-plugin/build.gradle @@ -1,7 +1,7 @@ apply plugin: "java" apply plugin: 'java-library' -apply plugin: "maven" apply plugin: "signing" +apply plugin: "maven" group = 'com.bladecoder.engine' @@ -34,7 +34,7 @@ jar { dependencies { api "com.badlogicgames.gdx:gdx:$libgdxVersion" - api "com.esotericsoftware.spine:spine-libgdx:3.7.83.1" + api "com.esotericsoftware.spine:spine-libgdx:3.8.55.1" implementation project(":blade-engine") } @@ -52,7 +52,7 @@ task sourcesJar(type: Jar) { } artifacts { - archives jar + //archives jar archives enginedocJar archives sourcesJar } diff --git a/blade-engine-spine-plugin/src/main/java/com/bladecoder/engine/spine/SpineRenderer.java b/blade-engine-spine-plugin/src/main/java/com/bladecoder/engine/spine/SpineRenderer.java index 08ac75376..579abc2e1 100644 --- a/blade-engine-spine-plugin/src/main/java/com/bladecoder/engine/spine/SpineRenderer.java +++ b/blade-engine-spine-plugin/src/main/java/com/bladecoder/engine/spine/SpineRenderer.java @@ -309,18 +309,18 @@ public void setSkin(String skin) { // Get the source skins. Skin singleSkin = skeletonData.findSkin(sk.trim()); - combinedSkin.addAttachments(singleSkin); - - // Set and apply the Skin to the skeleton. - sce.skeleton.setSkin(combinedSkin); + combinedSkin.addSkin(singleSkin); } + + // Set and apply the Skin to the skeleton. + sce.skeleton.setSkin(combinedSkin); } } else { sce.skeleton.setSkin((Skin) null); } - sce.skeleton.setSlotsToSetupPose(); + // sce.skeleton.setSlotsToSetupPose(); } this.skin = skin; @@ -446,9 +446,24 @@ public void setSecondaryAnimation(String animation) { } } + public Skeleton getCurrentSkeleton() { + SkeletonCacheEntry cs = (SkeletonCacheEntry) currentSource; + return cs.skeleton; + } + + public AnimationState getCurrentAnimationState() { + SkeletonCacheEntry cs = (SkeletonCacheEntry) currentSource; + return cs.animation; + } + private void setCurrentAnimation() { try { SkeletonCacheEntry cs = (SkeletonCacheEntry) currentSource; + + if (skin != null && (cs.skeleton.getSkin() == null || !skin.equals(cs.skeleton.getSkin().getName()))) { + setSkin(skin); + } + cs.skeleton.setToSetupPose(); cs.skeleton.setScaleX(flipX ? -1 : 1); cs.animation.setTimeScale(currentAnimation.duration); @@ -660,9 +675,6 @@ public void retrieveAssets() { } else if (initAnimation != null) { startAnimation(initAnimation, Tween.Type.SPRITE_DEFINED, 1, null); } - - setSkin(skin); - computeBbox(); } @Override @@ -692,8 +704,9 @@ public void write(Json json) { } else { - if (animationCb != null) - json.writeValue("cb", ActionCallbackSerializer.find(bjson.getWorld(), animationCb)); + if (animationCb != null) { + json.writeValue("cb", ActionCallbackSerializer.find(bjson.getWorld(), bjson.getScene(), animationCb)); + } json.writeValue("currentCount", currentCount); @@ -721,7 +734,7 @@ public void read(Json json, JsonValue jsonData) { world = bjson.getWorld(); } else { - animationCb = ActionCallbackSerializer.find(((BladeJson) json).getWorld(), + animationCb = ActionCallbackSerializer.find(((BladeJson) json).getWorld(), ((BladeJson) json).getScene(), json.readValue("cb", String.class, jsonData)); currentCount = json.readValue("currentCount", Integer.class, jsonData); diff --git a/blade-engine/build.gradle b/blade-engine/build.gradle index d720a9411..593a76131 100644 --- a/blade-engine/build.gradle +++ b/blade-engine/build.gradle @@ -1,7 +1,7 @@ apply plugin: 'java-library' apply plugin: "java" -apply plugin: "maven" apply plugin: "signing" +apply plugin: "maven" group = 'com.bladecoder.engine' @@ -43,6 +43,7 @@ javadoc { dependencies { api "com.badlogicgames.gdx:gdx:$libgdxVersion" + implementation "com.badlogicgames.gdx-controllers:gdx-controllers-core:$gdxControllersVersion" implementation "com.badlogicgames.gdx:gdx-freetype:$libgdxVersion" implementation "com.bladecoder.ink:blade-ink:$bladeInkVersion" api fileTree(dir: 'libs', include: '*.jar') @@ -61,7 +62,7 @@ task sourcesJar(type: Jar) { } artifacts { - archives jar + //archives jar archives enginedocJar archives sourcesJar } diff --git a/blade-engine/src/com/bladecoder/engine/BladeEngine.java b/blade-engine/src/com/bladecoder/engine/BladeEngine.java index d20757564..395ec73ff 100644 --- a/blade-engine/src/com/bladecoder/engine/BladeEngine.java +++ b/blade-engine/src/com/bladecoder/engine/BladeEngine.java @@ -19,6 +19,7 @@ import java.nio.IntBuffer; import java.text.MessageFormat; +import com.badlogic.gdx.Application; import com.badlogic.gdx.ApplicationListener; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.GL20; @@ -77,20 +78,18 @@ public void forceResolution(String forceRes) { public UI getUI() { return ui; } - + public void loadGame(String baseFolder) { - if(ui != null) { + if (ui != null) { ui.dispose(); world.dispose(); } - + world = new World(); - - if(baseFolder != null) { - EngineAssetManager.setAssetFolder(baseFolder); - Config.load(); - } - + + EngineAssetManager.setAssetFolder(baseFolder); + Config.getInstance().load(); + try { world.loadWorldDesc(); } catch (Exception e) { @@ -98,22 +97,24 @@ public void loadGame(String baseFolder) { EngineLogger.error("EXITING: " + e.getMessage()); Gdx.app.exit(); } - + ui = new UI(world); } @Override public void create() { if (!debug) - debug = Config.getProperty(Config.DEBUG_PROP, debug); + debug = Config.getInstance().getProperty(Config.DEBUG_PROP, debug); if (debug) EngineLogger.setDebug(); + else + EngineLogger.setDebugLevel(Application.LOG_ERROR); EngineLogger.debug("GAME CREATE"); if (forceRes == null) - forceRes = Config.getProperty(Config.FORCE_RES_PROP, forceRes); + forceRes = Config.getInstance().getProperty(Config.FORCE_RES_PROP, forceRes); if (forceRes != null) { EngineAssetManager.getInstance().forceResolution(forceRes); @@ -123,10 +124,10 @@ public void create() { if (EngineLogger.debugMode()) { if (chapter == null) - chapter = Config.getProperty(Config.CHAPTER_PROP, chapter); + chapter = Config.getInstance().getProperty(Config.CHAPTER_PROP, chapter); if (testScene == null) { - testScene = Config.getProperty(Config.TEST_SCENE_PROP, testScene); + testScene = Config.getInstance().getProperty(Config.TEST_SCENE_PROP, testScene); } if (testScene != null || chapter != null) { @@ -142,7 +143,7 @@ public void create() { } if (gameState == null) - gameState = Config.getProperty(Config.LOAD_GAMESTATE_PROP, gameState); + gameState = Config.getInstance().getProperty(Config.LOAD_GAMESTATE_PROP, gameState); if (gameState != null) { try { @@ -155,7 +156,7 @@ public void create() { if (restart) { try { world.getSerializer().loadChapter(); - + ui.setCurrentScreen(UI.Screens.SCENE_SCREEN); } catch (Exception e) { EngineLogger.error("ERROR LOADING GAME", e); @@ -165,13 +166,13 @@ public void create() { } if (recordName == null) - recordName = Config.getProperty(Config.PLAY_RECORD_PROP, recordName); + recordName = Config.getInstance().getProperty(Config.PLAY_RECORD_PROP, recordName); if (recordName != null) { ui.getRecorder().setFilename(recordName); ui.getRecorder().load(); ui.getRecorder().setPlaying(true); - + ui.setCurrentScreen(UI.Screens.SCENE_SCREEN); } } @@ -185,9 +186,6 @@ public void create() { EngineLogger.debug("Density: " + Gdx.graphics.getDensity()); EngineLogger.debug("Size Multiplier: " + DPIUtils.getSizeMultiplier()); } - - // Capture back key - Gdx.input.setCatchBackKey(true); } @Override @@ -210,8 +208,8 @@ public void render() { @Override public void resize(int width, int height) { EngineLogger.debug(MessageFormat.format("GAME RESIZE {0}x{1}", width, height)); - - if(ui != null) + + if (ui != null) ui.resize(width, height); } @@ -220,8 +218,7 @@ public void pause() { boolean bot = ui.getTesterBot().isEnabled(); boolean r = ui.getRecorder().isPlaying(); - if (!world.isDisposed() && - ((!bot && !r) || EngineLogger.lastError != null)) { + if (!world.isDisposed() && ((!bot && !r) || EngineLogger.lastError != null)) { EngineLogger.debug("GAME PAUSE"); ui.pause(); try { diff --git a/blade-engine/src/com/bladecoder/engine/actions/ActionFactory.java b/blade-engine/src/com/bladecoder/engine/actions/ActionFactory.java index ea9531a9e..e2cc8d7b4 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/ActionFactory.java +++ b/blade-engine/src/com/bladecoder/engine/actions/ActionFactory.java @@ -17,6 +17,7 @@ import java.util.HashMap; +import com.badlogic.gdx.utils.ObjectMap; import com.badlogic.gdx.utils.reflect.ClassReflection; import com.badlogic.gdx.utils.reflect.ReflectionException; import com.bladecoder.engine.util.ActionUtils; @@ -25,20 +26,95 @@ public class ActionFactory { private static ClassLoader loader = ActionFactory.class.getClassLoader(); + private static ObjectMap> tagToClass = new ObjectMap<>(); + + static { + addClassTag(AlphaAnimAction.class); + addClassTag(AnimationAction.class); + addClassTag(CameraAction.class); + addClassTag(CancelVerbAction.class); + addClassTag(ChooseAction.class); + addClassTag(CommentAction.class); + addClassTag(DisableActionAction.class); + addClassTag(DropItemAction.class); + addClassTag(EndAction.class); + addClassTag(EndGameAction.class); + addClassTag(GotoAction.class); + addClassTag(IfAttrAction.class); + addClassTag(IfInkVariableAction.class); + addClassTag(IfPropertyAction.class); + addClassTag(IfSceneAttrAction.class); + addClassTag(InkNewStoryAction.class); + addClassTag(InkRunAction.class); + addClassTag(InkVariable.class); + addClassTag(LeaveAction.class); + addClassTag(LoadChapterAction.class); + addClassTag(LookAtAction.class); + addClassTag(MoveToSceneAction.class); + addClassTag(MusicAction.class); + addClassTag(MusicVolumeAction.class); + addClassTag(OpenURLAction.class); + addClassTag(PickUpAction.class); + addClassTag(PlaySoundAction.class); + addClassTag(PositionAction.class); + addClassTag(PositionAnimAction.class); + addClassTag(PropertyAction.class); + addClassTag(RandomPositionAction.class); + addClassTag(RemoveInventoryItemAction.class); + addClassTag(RepeatAction.class); + addClassTag(RotateAction.class); + addClassTag(RunOnceAction.class); + addClassTag(RunVerbAction.class); + addClassTag(SayAction.class); + addClassTag(SayDialogAction.class); + addClassTag(ScaleAction.class); + addClassTag(ScaleAnimActionXY.class); + addClassTag(ScreenPositionAction.class); + addClassTag(SetAchievementAction.class); + addClassTag(SetActorAttrAction.class); + addClassTag(SetCutmodeAction.class); + addClassTag(SetDialogOptionAttrAction.class); + addClassTag(SetPlayerAction.class); + addClassTag(SetSceneStateAction.class); + addClassTag(SetStateAction.class); + addClassTag(SetDescAction.class); + addClassTag(SetWalkzoneAction.class); + addClassTag(ShowInventoryAction.class); + addClassTag(SoundAction.class); + addClassTag(TalktoAction.class); + addClassTag(TextAction.class); + addClassTag(TintAnimAction.class); + addClassTag(TransitionAction.class); + addClassTag(WaitAction.class); + } + + private static void addClassTag(Class cls) { + tagToClass.put(ActionUtils.getName(cls), cls); + } + + public static ObjectMap> getClassTags() { + return tagToClass; + } public static void setActionClassLoader(ClassLoader loader) { ActionFactory.loader = loader; } - + public static ClassLoader getActionClassLoader() { return loader; } - public static Action createByClass(String className, HashMap params) throws ClassNotFoundException, ReflectionException { + public static Action create(String tag, HashMap params) + throws ClassNotFoundException, ReflectionException { Action a = null; - Class c = Class.forName(className, true, loader); + Class c = tagToClass.get(tag); + + if (c == null) { + c = Class.forName(tag, true, loader); + } + a = (Action) ClassReflection.newInstance(c); if (params != null) { @@ -48,9 +124,10 @@ public static Action createByClass(String className, HashMap par try { ActionUtils.setParam(a, key, value); - } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { - EngineLogger.error("Error Setting Action Param - Action:" + className + " Param: " + key - + " Value: " + value + " Msg: NOT FOUND " + e.getMessage()); + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException + | IllegalAccessException e) { + EngineLogger.error("Error Setting Action Param - Action:" + tag + " Param: " + key + " Value: " + + value + " Msg: NOT FOUND " + e.getMessage()); } } } diff --git a/blade-engine/src/com/bladecoder/engine/actions/BaseCallbackAction.java b/blade-engine/src/com/bladecoder/engine/actions/BaseCallbackAction.java index 6d5f0593d..8cc74ad5b 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/BaseCallbackAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/BaseCallbackAction.java @@ -18,6 +18,7 @@ import com.badlogic.gdx.utils.Json; import com.badlogic.gdx.utils.Json.Serializable; import com.badlogic.gdx.utils.JsonValue; +import com.bladecoder.engine.model.Scene; import com.bladecoder.engine.model.World; import com.bladecoder.engine.serialization.ActionCallbackSerializer; import com.bladecoder.engine.serialization.BladeJson; @@ -32,9 +33,9 @@ public abstract class BaseCallbackAction implements Action, ActionCallback, Seri @ActionProperty(required = true) @ActionPropertyDescription("If this param is 'false' the text is showed and the action continues inmediatly") private boolean wait = true; - + protected World w; - + @Override public void init(World w) { this.w = w; @@ -45,7 +46,7 @@ public void resume() { if (verbCb != null || sCb != null) { if (verbCb == null) { - verbCb = ActionCallbackSerializer.find(w, sCb); + verbCb = ActionCallbackSerializer.find(w, w.getCurrentScene(), sCb); } ActionCallback cb2 = verbCb; @@ -70,8 +71,11 @@ public boolean getWait() { @Override public void write(Json json) { - if(verbCb != null) - json.writeValue("cb", ActionCallbackSerializer.find(((BladeJson) json).getWorld(), verbCb)); + if (verbCb != null) { + World w = ((BladeJson) json).getWorld(); + Scene s = ((BladeJson) json).getScene(); + json.writeValue("cb", ActionCallbackSerializer.find(w, s, verbCb)); + } } @Override diff --git a/blade-engine/src/com/bladecoder/engine/actions/CameraAction.java b/blade-engine/src/com/bladecoder/engine/actions/CameraAction.java index dc81c4f3b..c2fcce5d5 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/CameraAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/CameraAction.java @@ -31,7 +31,7 @@ public class CameraAction implements Action { @ActionPropertyDescription("Sets the camera position relative to this actor.") @ActionProperty(type = Type.ACTOR) private String target; - + @ActionProperty @ActionPropertyDescription("The target position") private Vector2 pos; @@ -47,7 +47,7 @@ public class CameraAction implements Action { @ActionPropertyDescription("Sets the actor to follow. 'none' puts no actor to follow") @ActionProperty(type = Type.ACTOR) private String followActor; - + @ActionProperty @ActionPropertyDescription("The interpolation mode") private InterpolationMode interpolation; @@ -55,9 +55,9 @@ public class CameraAction implements Action { @ActionProperty(defaultValue = "true", required = true) @ActionPropertyDescription("If this param is 'false' the text is showed and the action continues inmediatly") private boolean wait = true; - + private World w; - + @Override public void init(World w) { this.w = w; @@ -65,53 +65,55 @@ public void init(World w) { @Override public boolean run(VerbRunner cb) { - + Vector2 pos2 = null; - + Float zoom2 = zoom; - - if(pos != null) + + if (pos != null) pos2 = new Vector2(pos); float scale = EngineAssetManager.getInstance().getScale(); SceneCamera camera = w.getSceneCamera(); - - if(zoom2 == null || zoom2 < 0) + + if (zoom2 == null || zoom2 < 0) zoom2 = camera.getZoom(); - - if(pos == null && target == null) { + + if (pos == null && target == null) { pos2 = new Vector2(camera.getPosition()); pos2.x /= scale; pos2.y /= scale; } - + if (target != null) { BaseActor target = w.getCurrentScene().getActor(this.target, false); - + float x = target.getX(); float y = target.getY(); - - if(target instanceof InteractiveActor) { + + if (target instanceof InteractiveActor) { Vector2 refPoint = ((InteractiveActor) target).getRefPoint(); - x+= refPoint.x; - y+= refPoint.y; + x += refPoint.x; + y += refPoint.y; } - - if(pos2 != null){ + + if (pos2 != null) { pos2.x += x; pos2.y += y; } else { - pos2 = new Vector2(x,y); + pos2 = new Vector2(x, y); } - } + } + + camera.stopAnim(); if (followActor != null) { if (followActor.equals("none")) w.getCurrentScene().setCameraFollowActor(null); else { - w.getCurrentScene().setCameraFollowActor((SpriteActor) w.getCurrentScene() - .getActor(followActor, false)); + w.getCurrentScene() + .setCameraFollowActor((SpriteActor) w.getCurrentScene().getActor(followActor, false)); } } @@ -120,9 +122,9 @@ public boolean run(VerbRunner cb) { camera.setPosition(pos2.x * scale, pos2.y * scale); return false; } else { - camera.startAnimation(pos2.x * scale, pos2.y * scale, zoom2, duration, interpolation, wait?cb:null); + camera.startAnimation(pos2.x * scale, pos2.y * scale, zoom2, duration, interpolation, wait ? cb : null); } - + return wait; } diff --git a/blade-engine/src/com/bladecoder/engine/actions/DropItemAction.java b/blade-engine/src/com/bladecoder/engine/actions/DropItemAction.java index 35b0f6be5..b2755e44b 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/DropItemAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/DropItemAction.java @@ -22,6 +22,7 @@ import com.bladecoder.engine.actions.Param.Type; import com.bladecoder.engine.assets.EngineAssetManager; import com.bladecoder.engine.model.BaseActor; +import com.bladecoder.engine.model.Inventory; import com.bladecoder.engine.model.Scene; import com.bladecoder.engine.model.VerbRunner; import com.bladecoder.engine.model.World; @@ -40,9 +41,13 @@ public class DropItemAction implements Action { @ActionProperty @ActionPropertyDescription("Position in the scene where de actor is dropped") private Vector2 pos; - + + @ActionProperty + @ActionPropertyDescription("Inventory name. If empty, the active inventory is used.") + private String inventory; + private World w; - + @Override public void init(World w) { this.w = w; @@ -57,36 +62,47 @@ public boolean run(VerbRunner cb) { else ts = w.getScene(scene); - + Inventory inv = null; + + if (inventory == null) { + inv = w.getInventory(); + } else { + inv = w.getInventories().get(inventory); + if (inv == null) { + EngineLogger.error("Inventory not found: " + inventory); + return false; + } + } + BaseActor a; - + if (actor != null) { - a = w.getInventory().get(actor); + a = inv.get(actor); if (a == null) { EngineLogger.error(MessageFormat.format("DropItemAction - Item not found: {0}", actor)); return false; } - removeActor(ts, a); + removeActor(inv, ts, a); } else { - int n = w.getInventory().getNumItems(); - - for(int i = n - 1; i >= 0; i--) { - a = w.getInventory().get(i); - - removeActor(ts, a); + int n = inv.getNumItems(); + + for (int i = n - 1; i >= 0; i--) { + a = inv.get(i); + + removeActor(inv, ts, a); } } return false; } - private void removeActor(Scene ts, BaseActor a) { + private void removeActor(Inventory inv, Scene ts, BaseActor a) { float scale = EngineAssetManager.getInstance().getScale(); - w.getInventory().removeItem(a.getId()); + inv.removeItem(a.getId()); if (ts != w.getCurrentScene() && w.getCachedScene(ts.getId()) == null && a instanceof Disposable) ((Disposable) a).dispose(); diff --git a/blade-engine/src/com/bladecoder/engine/actions/GotoAction.java b/blade-engine/src/com/bladecoder/engine/actions/GotoAction.java index 80e677162..44d2ad52d 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/GotoAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/GotoAction.java @@ -24,6 +24,7 @@ import com.bladecoder.engine.model.InteractiveActor; import com.bladecoder.engine.model.VerbRunner; import com.bladecoder.engine.model.World; +import com.bladecoder.engine.util.EngineLogger; @ActionDescription("Walks to the selected position") public class GotoAction implements Action { @@ -32,27 +33,27 @@ public enum Align { } @ActionPropertyDescription("The walking actor") - @ActionProperty(type = Type.CHARACTER_ACTOR, required=true) + @ActionProperty(type = Type.CHARACTER_ACTOR, required = true) private String actor; @ActionPropertyDescription("Walks to this actor") @ActionProperty(type = Type.ACTOR) private String target; - + @ActionProperty @ActionPropertyDescription("The absolute position to walk to if no target actor is selected. Relative to target if selected.") private Vector2 pos; - - @ActionProperty(required=true, defaultValue = "false") + + @ActionProperty(required = true, defaultValue = "false") @ActionPropertyDescription("Ignore the walking zone and walk in a straight line.") private boolean ignoreWalkZone = false; @ActionProperty(required = true, defaultValue = "true") @ActionPropertyDescription("If this param is 'false' the text is showed and the action continues inmediatly") private boolean wait = true; - + private World w; - + @Override public void init(World w) { this.w = w; @@ -62,35 +63,40 @@ public void init(World w) { public boolean run(VerbRunner cb) { CharacterActor actor = (CharacterActor) w.getCurrentScene().getActor(this.actor, false); - + float x = actor.getX(); float y = actor.getY(); if (target != null) { - BaseActor target = w.getCurrentScene().getActor(this.target, false); - - x = target.getX(); - y = target.getY(); - - if(target instanceof InteractiveActor && target != actor) { - Vector2 refPoint = ((InteractiveActor) target).getRefPoint(); - x+= refPoint.x; - y+= refPoint.y; + BaseActor targetActor = w.getCurrentScene().getActor(target, false); + + if (targetActor == null) { + EngineLogger.error(target + " not found in the current scene."); + return false; } - - if(pos != null){ + + x = targetActor.getX(); + y = targetActor.getY(); + + if (targetActor instanceof InteractiveActor && targetActor != actor) { + Vector2 refPoint = ((InteractiveActor) targetActor).getRefPoint(); + x += refPoint.x; + y += refPoint.y; + } + + if (pos != null) { float scale = EngineAssetManager.getInstance().getScale(); - + x += pos.x * scale; y += pos.y * scale; } - } else if(pos != null){ + } else if (pos != null) { float scale = EngineAssetManager.getInstance().getScale(); - + x = pos.x * scale; y = pos.y * scale; } - + actor.goTo(new Vector2(x, y), wait ? cb : null, ignoreWalkZone); return wait; diff --git a/blade-engine/src/com/bladecoder/engine/actions/IAchievementAPI.java b/blade-engine/src/com/bladecoder/engine/actions/IAchievementAPI.java new file mode 100644 index 000000000..13a9ce543 --- /dev/null +++ b/blade-engine/src/com/bladecoder/engine/actions/IAchievementAPI.java @@ -0,0 +1,7 @@ +package com.bladecoder.engine.actions; + +public interface IAchievementAPI { + + void setAchievement(String name); + +} diff --git a/blade-engine/src/com/bladecoder/engine/actions/IfAttrAction.java b/blade-engine/src/com/bladecoder/engine/actions/IfAttrAction.java index 7e962ac21..2db2a3b53 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/IfAttrAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/IfAttrAction.java @@ -18,19 +18,21 @@ import com.bladecoder.engine.model.AnimationRenderer; import com.bladecoder.engine.model.BaseActor; import com.bladecoder.engine.model.InteractiveActor; +import com.bladecoder.engine.model.Inventory; import com.bladecoder.engine.model.Scene; import com.bladecoder.engine.model.SpriteActor; import com.bladecoder.engine.model.VerbRunner; import com.bladecoder.engine.model.World; import com.bladecoder.engine.util.ActionUtils; import com.bladecoder.engine.util.EngineLogger; +import com.bladecoder.engine.util.PolygonUtils; @ActionDescription(name = "IfActorAttr", value = "Execute the actions inside the If/EndIf if the attribute has the specified value.") public class IfAttrAction extends AbstractIfAction { public static final String ENDTYPE_VALUE = "else"; public enum ActorAttribute { - STATE, VISIBLE, INTERACTIVE, IN_INVENTORY, TARGET, IN_SCENE, LAYER, DIRECTION, IN_UI + STATE, VISIBLE, INTERACTIVE, IN_INVENTORY, TARGET, IN_SCENE, LAYER, DIRECTION, IN_UI, INSIDE } @ActionProperty(required = true) @@ -57,25 +59,36 @@ public boolean run(VerbRunner cb) { Scene s = actor.getScene(w); final String actorId = actor.getActorId(); - if (actorId == null) { - // if called inside a scene verb and no actor is specified, return - EngineLogger.error(getClass() + ": No actor specified"); - return false; - } - BaseActor a = s.getActor(actorId, true); + if (attr.equals(ActorAttribute.STATE)) { + BaseActor a = s.getActor(actorId, true); + if (a == null || !(a instanceof InteractiveActor)) { + EngineLogger.error(getClass() + "- Actor not found: " + actorId); + return false; + } - if (attr.equals(ActorAttribute.STATE) && a instanceof InteractiveActor) { InteractiveActor ia = (InteractiveActor) a; if (!ActionUtils.compareNullStr(value, ia.getState())) { gotoElse(cb); } } else if (attr.equals(ActorAttribute.VISIBLE)) { + BaseActor a = s.getActor(actorId, true); + if (a == null) { + EngineLogger.error(getClass() + "- Actor not found: " + actorId); + return false; + } + boolean val = Boolean.parseBoolean(value); if (val != a.isVisible()) { gotoElse(cb); } } else if (attr.equals(ActorAttribute.INTERACTIVE)) { + BaseActor a = s.getActor(actorId, true); + if (a == null) { + EngineLogger.error(getClass() + "- Actor not found: " + actorId); + return false; + } + boolean val = Boolean.parseBoolean(value); if (a instanceof InteractiveActor) { @@ -86,12 +99,25 @@ public boolean run(VerbRunner cb) { gotoElse(cb); } } else if (attr.equals(ActorAttribute.IN_INVENTORY)) { - boolean val = Boolean.parseBoolean(value); + // 'value' can have the inventory name to search in + // or 'true/false' to search in the current inventory. + + Inventory inventory = null; + boolean val = true; + + if (value != null) { + inventory = w.getInventories().get(value); + } + + if (inventory == null) { + // boolean mode: search in the current inventory + val = Boolean.parseBoolean(value); + inventory = w.getInventory(); + } SpriteActor item = null; - if (a != null) - item = w.getInventory().get(a.getId()); + item = inventory.get(actorId); if ((val && item == null) || (!val && item != null)) { gotoElse(cb); @@ -115,12 +141,25 @@ public boolean run(VerbRunner cb) { if ((val && a2 == null) || (!val && a2 != null)) gotoElse(cb); - } else if (attr.equals(ActorAttribute.LAYER) && a instanceof InteractiveActor) { + } else if (attr.equals(ActorAttribute.LAYER)) { + BaseActor a = s.getActor(actorId, true); + if (a == null || !(a instanceof InteractiveActor)) { + EngineLogger.error(getClass() + "- No not found: " + actorId); + return false; + } + InteractiveActor ia = (InteractiveActor) a; if (!ActionUtils.compareNullStr(value, ia.getLayer())) { gotoElse(cb); } - } else if (attr.equals(ActorAttribute.DIRECTION) && a instanceof SpriteActor) { + } else if (attr.equals(ActorAttribute.DIRECTION)) { + + BaseActor a = s.getActor(actorId, true); + if (a == null || !(a instanceof SpriteActor)) { + EngineLogger.error(getClass() + "- No not found: " + actorId); + return false; + } + SpriteActor sa = (SpriteActor) a; if (sa.getRenderer() instanceof AnimationRenderer) { @@ -136,6 +175,25 @@ public boolean run(VerbRunner cb) { gotoElse(cb); } } + } else if (attr.equals(ActorAttribute.INSIDE)) { + BaseActor a = s.getActor(actorId, true); + if (a == null) { + EngineLogger.error(getClass() + "- No not found: " + actorId); + return false; + } + + BaseActor insideActor = w.getCurrentScene().getActor(value, false); + + if (insideActor == null) { + EngineLogger.debug("Actor for inside test not found: " + value); + return false; + } + + boolean inside = PolygonUtils.isPointInside(insideActor.getBBox(), a.getX(), a.getY(), true); + + if (!inside) { + gotoElse(cb); + } } return false; diff --git a/blade-engine/src/com/bladecoder/engine/actions/IfPropertyAction.java b/blade-engine/src/com/bladecoder/engine/actions/IfPropertyAction.java index 56516ff05..5dbdbb9ac 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/IfPropertyAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/IfPropertyAction.java @@ -20,7 +20,7 @@ import com.bladecoder.engine.util.ActionUtils; import com.bladecoder.engine.util.Config; -@ActionDescription("Execute actions inside the If/EndIf if the game property has the specified value. Properties are created with the 'Property' action or set in the 'BladeEngine.properties' file. The next always exists: SAVED_GAME_VERSION, PREVIOUS_SCENE, CURRENT_CHAPTER, PLATFORM") +@ActionDescription("Execute actions inside the If/EndIf if the game property has the specified value. Properties are created with the 'Property' action or set in the 'BladeEngine.properties' file. The next always exists: SAVED_GAME_VERSION, PREVIOUS_SCENE, CURRENT_CHAPTER, PLATFORM.") public class IfPropertyAction extends AbstractIfAction { @ActionProperty(required = true) @ActionPropertyDescription("The property name") @@ -29,9 +29,9 @@ public class IfPropertyAction extends AbstractIfAction { @ActionProperty @ActionPropertyDescription("The property value") private String value; - + private World w; - + @Override public void init(World w) { this.w = w; @@ -40,12 +40,15 @@ public void init(World w) { @Override public boolean run(VerbRunner cb) { String valDest = w.getCustomProperty(name); - - if(valDest == null) - valDest = Config.getProperty(name, null); - + + if (valDest == null) + valDest = Config.getInstance().getProperty(name, null); + + if (valDest == null) + valDest = Config.getInstance().getPref(name, null); + if (!ActionUtils.compareNullStr(value, valDest)) { - gotoElse((VerbRunner) cb); + gotoElse(cb); } return false; diff --git a/blade-engine/src/com/bladecoder/engine/actions/IfSceneAttrAction.java b/blade-engine/src/com/bladecoder/engine/actions/IfSceneAttrAction.java index 2a2db5009..665f0ba13 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/IfSceneAttrAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/IfSceneAttrAction.java @@ -40,9 +40,9 @@ public enum SceneAttr { @ActionProperty @ActionPropertyDescription("The attribute value") private String value; - + private World w; - + @Override public void init(World w) { this.w = w; @@ -50,27 +50,27 @@ public void init(World w) { @Override public boolean run(VerbRunner cb) { - Scene s = (scene != null && !scene.isEmpty()) ? w.getScene(scene) : w - .getCurrentScene(); + Scene s = (scene != null && !scene.isEmpty()) ? w.getScene(scene) : w.getCurrentScene(); if (attr == SceneAttr.STATE) { if (!ActionUtils.compareNullStr(value, s.getState())) { - gotoElse((VerbRunner) cb); + gotoElse(cb); } } else if (attr == SceneAttr.CURRENT_SCENE) { String scn = w.getCurrentScene().getId(); - - if (!ActionUtils.compareNullStr(value, scn)) { - gotoElse((VerbRunner) cb); + + if (((value != null && !value.isEmpty()) && !ActionUtils.compareNullStr(value, scn)) + || (scene != null && !scene.isEmpty() && !ActionUtils.compareNullStr(s.getId(), scn))) { + gotoElse(cb); } } else if (attr == SceneAttr.PLAYER) { CharacterActor player = s.getPlayer(); - - String id = player!=null?player.getId():null; - + + String id = player != null ? player.getId() : null; + if (!ActionUtils.compareNullStr(value, id)) { - gotoElse((VerbRunner) cb); - } + gotoElse(cb); + } } return false; diff --git a/blade-engine/src/com/bladecoder/engine/actions/LeaveAction.java b/blade-engine/src/com/bladecoder/engine/actions/LeaveAction.java index c93f0b1a7..69f590436 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/LeaveAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/LeaveAction.java @@ -24,13 +24,17 @@ public class LeaveAction implements Action { @ActionPropertyDescription("The target scene") @ActionProperty(type = Type.SCENE, required = true) private String scene; - + @ActionPropertyDescription("Inits the scene and run the 'init' verb") @ActionProperty(defaultValue = "true", required = true) private boolean init = true; - + + @ActionPropertyDescription("The verb to run after loading. If null, 'init' verb will be run but only if init=true") + @ActionProperty + private String initVerb = null; + private World w; - + @Override public void init(World w) { this.w = w; @@ -38,7 +42,7 @@ public void init(World w) { @Override public boolean run(VerbRunner cb) { - w.setCurrentScene(scene, init); + w.setCurrentScene(scene, init, initVerb); return true; } diff --git a/blade-engine/src/com/bladecoder/engine/actions/MoveToSceneAction.java b/blade-engine/src/com/bladecoder/engine/actions/MoveToSceneAction.java index 674f71532..0ccec89dd 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/MoveToSceneAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/MoveToSceneAction.java @@ -19,7 +19,7 @@ import com.bladecoder.engine.actions.Param.Type; import com.bladecoder.engine.assets.AssetConsumer; import com.bladecoder.engine.assets.EngineAssetManager; -import com.bladecoder.engine.model.InteractiveActor; +import com.bladecoder.engine.model.BaseActor; import com.bladecoder.engine.model.Scene; import com.bladecoder.engine.model.VerbRunner; import com.bladecoder.engine.model.World; @@ -27,60 +27,68 @@ @ActionDescription("Move the actor to the selected scene") public class MoveToSceneAction implements Action { - @ActionProperty(required=true) - @ActionPropertyDescription("The selected actor") + @ActionProperty(required = true) + @ActionPropertyDescription("The selected actor") private SceneActorRef actor; @ActionPropertyDescription("The target scene. The current scene if empty.") @ActionProperty(type = Type.SCENE) private String scene; - + private World w; - + @Override public void init(World w) { this.w = w; } @Override - public boolean run(VerbRunner cb) { + public boolean run(VerbRunner cb) { final Scene s = actor.getScene(w); - final String actorId = actor.getActorId(); - + final String actorId = actor.getActorId(); + if (actorId == null) { // if called in a scene verb and no actor is specified, we do nothing EngineLogger.error(getClass() + ": No actor specified"); return false; } - InteractiveActor a = (InteractiveActor)s.getActor(actorId, false); + BaseActor a = s.getActor(actorId, false); + + if (a == null) { + EngineLogger.error(getClass() + "- Actor not found: " + actorId + " in scene: " + s.getId()); + return false; + } s.removeActor(a); - - if(s == w.getCurrentScene() && a instanceof Disposable) - ((Disposable) a).dispose(); - - Scene ts = null; - - if(scene == null) + + Scene ts = null; + + if (scene == null) ts = w.getCurrentScene(); else ts = w.getScene(scene); - + + // Dispose if s is the current scene or a cached scene and the target is not the + // current scene or a cache scene + if ((s == w.getCurrentScene() || w.getCachedScene(ts.getId()) != null) + && !(ts == w.getCurrentScene() || w.getCachedScene(ts.getId()) != null) && a instanceof Disposable) { + ((Disposable) a).dispose(); + } + // We must load assets when the target scene is the current scene or when // the scene is cached. - if((ts == w.getCurrentScene() || - w.getCachedScene(ts.getId()) != null) && a instanceof AssetConsumer) { + if ((ts == w.getCurrentScene() || w.getCachedScene(ts.getId()) != null) + && !(s == w.getCurrentScene() || w.getCachedScene(s.getId()) != null) && a instanceof AssetConsumer) { ((AssetConsumer) a).loadAssets(); EngineAssetManager.getInstance().finishLoading(); ((AssetConsumer) a).retrieveAssets(); } - + ts.addActor(a); - + return false; } - } diff --git a/blade-engine/src/com/bladecoder/engine/actions/PickUpAction.java b/blade-engine/src/com/bladecoder/engine/actions/PickUpAction.java index b3d1a8fa6..de1031534 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/PickUpAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/PickUpAction.java @@ -19,6 +19,7 @@ import com.bladecoder.engine.assets.EngineAssetManager; import com.bladecoder.engine.model.AnimationRenderer; import com.bladecoder.engine.model.InteractiveActor; +import com.bladecoder.engine.model.Inventory; import com.bladecoder.engine.model.Scene; import com.bladecoder.engine.model.SpriteActor; import com.bladecoder.engine.model.VerbRunner; @@ -34,9 +35,13 @@ public class PickUpAction implements Action { @ActionProperty @ActionPropertyDescription("The animation/sprite to show while in inventory. If empty, the animation will be 'actorid.inventory'") private String animation; - + + @ActionProperty + @ActionPropertyDescription("Inventory name. If empty, the active inventory is used.") + private String inventory; + private World w; - + @Override public void init(World w) { this.w = w; @@ -57,10 +62,8 @@ public boolean run(VerbRunner cb) { if (actor instanceof SpriteActor) { SpriteActor a = (SpriteActor) actor; - - if (scn != w.getCurrentScene() && - w.getCachedScene(scn.getId()) == null - ) { + + if (scn != w.getCurrentScene() && w.getCachedScene(scn.getId()) == null) { a.loadAssets(); EngineAssetManager.getInstance().finishLoading(); a.retrieveAssets(); @@ -73,7 +76,19 @@ else if (((AnimationRenderer) a.getRenderer()).getAnimations().get(a.getId() + " a.startAnimation(a.getId() + ".inventory", null); } - w.getInventory().addItem(a); + Inventory inv = null; + + if (inventory == null) { + inv = w.getInventory(); + } else { + inv = w.getInventories().get(inventory); + if (inv == null) { + EngineLogger.error("Inventory not found: " + inventory); + return false; + } + } + + inv.addItem(a); } return false; diff --git a/blade-engine/src/com/bladecoder/engine/actions/PropertyAction.java b/blade-engine/src/com/bladecoder/engine/actions/PropertyAction.java index 2310714ae..743fef4e8 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/PropertyAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/PropertyAction.java @@ -22,16 +22,14 @@ public class PropertyAction implements Action { @ActionProperty(required = true) @ActionPropertyDescription("Property name") - private String prop; - @ActionProperty(required = true) + @ActionProperty @ActionPropertyDescription("Property value") - private String value; - + private World w; - + @Override public void init(World w) { this.w = w; diff --git a/blade-engine/src/com/bladecoder/engine/actions/RandomPositionAction.java b/blade-engine/src/com/bladecoder/engine/actions/RandomPositionAction.java new file mode 100644 index 000000000..5089916f3 --- /dev/null +++ b/blade-engine/src/com/bladecoder/engine/actions/RandomPositionAction.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright 2014 Rafael Garcia Moreno. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package com.bladecoder.engine.actions; + +import com.badlogic.gdx.math.Vector2; +import com.bladecoder.engine.assets.EngineAssetManager; +import com.bladecoder.engine.model.BaseActor; +import com.bladecoder.engine.model.InteractiveActor; +import com.bladecoder.engine.model.Scene; +import com.bladecoder.engine.model.VerbRunner; +import com.bladecoder.engine.model.World; + +@ActionDescription("Sets actor position randomly.") +public class RandomPositionAction implements Action { + @ActionProperty(required = true) + @ActionPropertyDescription("The actor to change his position") + private SceneActorRef actor; + + @ActionProperty + @ActionPropertyDescription("Obtain the target position from this actor.") + private SceneActorRef target; + + @ActionProperty(required = true) + @ActionPropertyDescription("Maximum xy values. The absolute position to set if no target is selected. Relative if target is selected.") + private Vector2 maxPosition; + + @ActionProperty(required = true) + @ActionPropertyDescription("Minimum xy values. The absolute position to set if no target is selected. Relative if target is selected.") + private Vector2 minPosition; + + private World w; + + @Override + public void init(World w) { + this.w = w; + } + + @Override + public boolean run(VerbRunner cb) { + Scene s = actor.getScene(w); + + BaseActor a = s.getActor(actor.getActorId(), true); + + float x = a.getX(); + float y = a.getY(); + + float rx = (float) (minPosition.x + Math.random() * (maxPosition.x - minPosition.x)); + float ry = (float) (minPosition.y + Math.random() * (maxPosition.y - minPosition.y)); + + if (target != null) { + Scene ts = target.getScene(w); + BaseActor anchorActor = ts.getActor(target.getActorId(), false); + + x = anchorActor.getX(); + y = anchorActor.getY(); + + if (anchorActor instanceof InteractiveActor && a != anchorActor) { + Vector2 refPoint = ((InteractiveActor) anchorActor).getRefPoint(); + x += refPoint.x; + y += refPoint.y; + } + + float scale = EngineAssetManager.getInstance().getScale(); + + x += rx * scale; + y += ry * scale; + } else { + float scale = EngineAssetManager.getInstance().getScale(); + x = rx * scale; + y = ry * scale; + } + + a.setPosition(x, y); + + return false; + } + +} diff --git a/blade-engine/src/com/bladecoder/engine/actions/RotateAction.java b/blade-engine/src/com/bladecoder/engine/actions/RotateAction.java index 326b99055..765912879 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/RotateAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/RotateAction.java @@ -23,7 +23,7 @@ import com.bladecoder.engine.model.World; import com.bladecoder.engine.util.InterpolationMode; -@ActionDescription(name = "RotateAnim", value= "Sets an actor Rotate animation") +@ActionDescription(name = "RotateAnim", value = "Sets an actor Rotate animation") public class RotateAction implements Action { @ActionPropertyDescription("The target actor") @ActionProperty(type = Type.SPRITE_ACTOR) @@ -52,24 +52,23 @@ public class RotateAction implements Action { @ActionProperty @ActionPropertyDescription("The interpolation mode") private InterpolationMode interpolation; - + private World w; - + @Override public void init(World w) { this.w = w; } @Override - public boolean run(VerbRunner cb) { - SpriteActor a = (SpriteActor) w.getCurrentScene().getActor(actor, false); - + public boolean run(VerbRunner cb) { + SpriteActor a = (SpriteActor) w.getCurrentScene().getActor(actor, true); + SpriteRotateTween t = new SpriteRotateTween(); - t.start(a, repeat, count, rotation, speed, interpolation, - wait ? cb : null); - + t.start(a, repeat, count, rotation, speed, interpolation, wait ? cb : null); + a.addTween(t); - + return wait; } diff --git a/blade-engine/src/com/bladecoder/engine/actions/RunVerbAction.java b/blade-engine/src/com/bladecoder/engine/actions/RunVerbAction.java index 4decc1229..c5cea22c8 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/RunVerbAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/RunVerbAction.java @@ -39,13 +39,13 @@ public class RunVerbAction implements VerbRunner, Action { @ActionProperty @ActionPropertyDescription("Aditional actor for 'use' verb") private String target; - + @ActionProperty(required = true) @ActionPropertyDescription("If this param is 'false' the text is showed and the action continues inmediatly") private boolean wait = true; - + private World w; - + @Override public void init(World w) { this.w = w; @@ -53,19 +53,19 @@ public void init(World w) { @Override public boolean run(VerbRunner cb) { - - run(cb.getCurrentTarget(), wait?cb:null); - + + run(cb.getCurrentTarget(), wait ? cb : null); + return wait; } private Verb getVerb() { Verb v = null; - + Scene s = w.getCurrentScene(); if (actor != null) { - InteractiveActor a = (InteractiveActor)s.getActor(actor, true); + InteractiveActor a = (InteractiveActor) s.getActor(actor, true); v = a.getVerbManager().getVerb(verb, a.getState(), target); } @@ -78,8 +78,9 @@ private Verb getVerb() { v = w.getVerbManager().getVerb(verb, null, target); } - if (v == null) + if (v == null) { EngineLogger.error("Cannot find VERB: " + verb + " for ACTOR: " + actor); + } return v; } @@ -91,16 +92,15 @@ public void resume() { @Override public void cancel() { - + // check if the actor has been moved during the execution - if(actor != null) { - InteractiveActor a = (InteractiveActor)w.getCurrentScene().getActor(actor, true); - - if(a == null) + if (actor != null) { + InteractiveActor a = (InteractiveActor) w.getCurrentScene().getActor(actor, true); + + if (a == null) return; } - - + getVerb().cancel(); } @@ -114,9 +114,10 @@ public ArrayList getActions() { Verb v = getVerb(); if (v == null) { - if( actor != null) - EngineLogger.error(MessageFormat.format("Verb ''{0}'' not found for actor ''{1}({3})'' and target ''{2}''.", - verb, actor, target, ((InteractiveActor)w.getCurrentScene().getActor(actor, true)).getState())); + if (actor != null) + EngineLogger.error(MessageFormat.format( + "Verb ''{0}'' not found for actor ''{1}({3})'' and target ''{2}''.", verb, actor, target, + ((InteractiveActor) w.getCurrentScene().getActor(actor, true)).getState())); else EngineLogger.error(MessageFormat.format("Verb ''{0}'' not found.", verb)); @@ -129,8 +130,10 @@ public ArrayList getActions() { @Override public void run(String currentTarget, ActionCallback cb) { Verb v = getVerb(); - - v.run(currentTarget, cb); + + if (v != null) { + v.run(currentTarget, cb); + } } @Override @@ -141,6 +144,6 @@ public int getIP() { @Override public void setIP(int ip) { getVerb().setIP(ip); - } + } } diff --git a/blade-engine/src/com/bladecoder/engine/actions/ScaleAction.java b/blade-engine/src/com/bladecoder/engine/actions/ScaleAction.java index a85352ed3..e6b8ee3cd 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/ScaleAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/ScaleAction.java @@ -23,7 +23,7 @@ import com.bladecoder.engine.model.World; import com.bladecoder.engine.util.InterpolationMode; -@ActionDescription(name = "ScaleAnim", value= "Proportional Scale animation for sprite actors") +@ActionDescription(name = "ScaleAnim", value = "Proportional Scale animation for sprite actors") public class ScaleAction implements Action { @ActionPropertyDescription("The target actor") @ActionProperty(type = Type.SPRITE_ACTOR) @@ -39,37 +39,36 @@ public class ScaleAction implements Action { @ActionProperty @ActionPropertyDescription("The The times to repeat") - private int count = 1; + private int count = -1; @ActionProperty(required = true) @ActionPropertyDescription("If this param is 'false' the transition is showed and the action continues inmediatly") private boolean wait = true; - @ActionProperty(required = true, defaultValue = "REPEAT") + @ActionProperty(required = true, defaultValue = "NO_REPEAT") @ActionPropertyDescription("The repeat mode") - private Tween.Type repeat = Tween.Type.REPEAT; + private Tween.Type repeat = Tween.Type.NO_REPEAT; @ActionProperty @ActionPropertyDescription("The interpolation mode") private InterpolationMode interpolation; - + private World w; - + @Override public void init(World w) { this.w = w; } @Override - public boolean run(VerbRunner cb) { - SpriteActor a = (SpriteActor) w.getCurrentScene().getActor(actor, false); - + public boolean run(VerbRunner cb) { + SpriteActor a = (SpriteActor) w.getCurrentScene().getActor(actor, true); + SpriteScaleTween t = new SpriteScaleTween(); - t.start(a, repeat, count, scale, scale, speed, interpolation, - wait ? cb : null); - + t.start(a, repeat, count, scale, scale, speed, interpolation, wait ? cb : null); + a.addTween(t); - + return wait; } diff --git a/blade-engine/src/com/bladecoder/engine/actions/ScaleAnimActionXY.java b/blade-engine/src/com/bladecoder/engine/actions/ScaleAnimActionXY.java index d6078e67d..1215f3dd7 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/ScaleAnimActionXY.java +++ b/blade-engine/src/com/bladecoder/engine/actions/ScaleAnimActionXY.java @@ -24,7 +24,7 @@ import com.bladecoder.engine.model.World; import com.bladecoder.engine.util.InterpolationMode; -@ActionDescription(name = "ScaleAnimXY", value= "Scale animation for sprite actors") +@ActionDescription(name = "ScaleAnimXY", value = "Scale animation for sprite actors") public class ScaleAnimActionXY implements Action { @ActionPropertyDescription("The target actor") @ActionProperty(type = Type.SPRITE_ACTOR) @@ -53,24 +53,23 @@ public class ScaleAnimActionXY implements Action { @ActionProperty @ActionPropertyDescription("The interpolation mode") private InterpolationMode interpolation; - + private World w; - + @Override public void init(World w) { this.w = w; } @Override - public boolean run(VerbRunner cb) { - SpriteActor a = (SpriteActor) w.getCurrentScene().getActor(actor, false); - + public boolean run(VerbRunner cb) { + SpriteActor a = (SpriteActor) w.getCurrentScene().getActor(actor, true); + SpriteScaleTween t = new SpriteScaleTween(); - t.start(a, repeat, count, scale.x, scale.y, speed, interpolation, - wait ? cb : null); - + t.start(a, repeat, count, scale.x, scale.y, speed, interpolation, wait ? cb : null); + a.addTween(t); - + return wait; } diff --git a/blade-engine/src/com/bladecoder/engine/actions/ScreenPositionAction.java b/blade-engine/src/com/bladecoder/engine/actions/ScreenPositionAction.java index d4beb0a75..0dd01ceaf 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/ScreenPositionAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/ScreenPositionAction.java @@ -95,12 +95,9 @@ public boolean run(VerbRunner cb) { v.y += viewport.getWorldHeight() / 2; } - // viewport.project(v); - v.x *= viewport.getScreenWidth() / viewport.getWorldWidth(); v.y *= viewport.getScreenHeight() / viewport.getWorldHeight(); - // v.y = viewport.getScreenHeight() - v.y; v.y = Gdx.graphics.getHeight() - v.y; if (w.getUIActors().getActors().contains(a)) diff --git a/blade-engine/src/com/bladecoder/engine/actions/SetAchievementAction.java b/blade-engine/src/com/bladecoder/engine/actions/SetAchievementAction.java new file mode 100644 index 000000000..146e698da --- /dev/null +++ b/blade-engine/src/com/bladecoder/engine/actions/SetAchievementAction.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright 2014 Rafael Garcia Moreno. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package com.bladecoder.engine.actions; + +import com.bladecoder.engine.model.VerbRunner; +import com.bladecoder.engine.model.World; + +@ActionDescription("Set an achievement.") +public class SetAchievementAction implements Action { + public static IAchievementAPI achievementAPI; + + @ActionProperty(required = true) + @ActionPropertyDescription("Achievement name") + private String name; + + @Override + public void init(World w) { + } + + @Override + public boolean run(VerbRunner cb) { + if (achievementAPI != null) + achievementAPI.setAchievement(name); + + return false; + } + +} diff --git a/blade-engine/src/com/bladecoder/engine/actions/SetActorAttrAction.java b/blade-engine/src/com/bladecoder/engine/actions/SetActorAttrAction.java index c06384096..7e9101708 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/SetActorAttrAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/SetActorAttrAction.java @@ -60,7 +60,7 @@ public class SetActorAttrAction implements Action { @ActionProperty @ActionPropertyDescription("Sets the actor scale proportionally") private Float scale; - + @ActionProperty @ActionPropertyDescription("Sets the actor scale non proportionally") private Vector2 scaleXY; @@ -91,13 +91,13 @@ public class SetActorAttrAction implements Action { @ActionProperty @ActionPropertyDescription("Sets the actor speed for walking. Only supported for character actors.") private Float walkingSpeed; - + @ActionProperty @ActionPropertyDescription("Sets the position of the text when talking. Relative to the character position.") private Vector2 talkingTextPos; - + private World w; - + @Override public void init(World w) { this.w = w; @@ -163,7 +163,7 @@ public boolean run(VerbRunner cb) { else EngineLogger.error("'scale' property not supported for actor:" + a.getId()); } - + if (scaleXY != null) { if (a instanceof SpriteActor) ((SpriteActor) a).setScale(scaleXY.x, scaleXY.y); @@ -180,7 +180,7 @@ public boolean run(VerbRunner cb) { if (tint != null) { if (a instanceof SpriteActor) - ((SpriteActor) a).setTint(tint); + ((SpriteActor) a).setTint(tint.cpy()); else EngineLogger.error("'tint' property not supported for actor:" + a.getId()); } @@ -229,7 +229,7 @@ public boolean run(VerbRunner cb) { } else EngineLogger.error("'uiActor' property not supported for actor:" + a.getId()); } - + if (talkingTextPos != null) { if (a instanceof CharacterActor) ((CharacterActor) a).setTalkingTextPos(talkingTextPos); @@ -244,8 +244,7 @@ private void setUIActor(Scene scn, InteractiveActor actor) { scn.removeActor(actor); - if (scn != w.getCurrentScene() && w.getCachedScene(scn.getId()) == null - && actor instanceof AssetConsumer) { + if (scn != w.getCurrentScene() && w.getCachedScene(scn.getId()) == null && actor instanceof AssetConsumer) { ((AssetConsumer) actor).loadAssets(); EngineAssetManager.getInstance().finishLoading(); ((AssetConsumer) actor).retrieveAssets(); diff --git a/blade-engine/src/com/bladecoder/engine/actions/SetDescAction.java b/blade-engine/src/com/bladecoder/engine/actions/SetDescAction.java new file mode 100644 index 000000000..99e630f1a --- /dev/null +++ b/blade-engine/src/com/bladecoder/engine/actions/SetDescAction.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright 2014 Rafael Garcia Moreno. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package com.bladecoder.engine.actions; + +import com.bladecoder.engine.actions.Param.Type; +import com.bladecoder.engine.model.InteractiveActor; +import com.bladecoder.engine.model.Scene; +import com.bladecoder.engine.model.VerbRunner; +import com.bladecoder.engine.model.World; +import com.bladecoder.engine.util.EngineLogger; + +@ActionDescription(value = "Sets the actor description") +public class SetDescAction implements Action { + @ActionProperty(type = Type.SCENE_INTERACTIVE_ACTOR, required = true) + @ActionPropertyDescription("The target actor") + private SceneActorRef actor; + + @ActionProperty + @ActionPropertyDescription("The actor 'desc'") + private String text; + + private World w; + + @Override + public void init(World w) { + this.w = w; + } + + @Override + public boolean run(VerbRunner cb) { + final Scene s = actor.getScene(w); + + String actorId = actor.getActorId(); + if (actorId == null) { + EngineLogger.error("SetDesc - Actor not set."); + return false; + } + + InteractiveActor a = (InteractiveActor) s.getActor(actorId, true); + + if (a != null) + a.setDesc(text); + else + EngineLogger.error("SetDesc - Actor not found: " + actorId); + + return false; + } + +} diff --git a/blade-engine/src/com/bladecoder/engine/actions/SetPlayerAction.java b/blade-engine/src/com/bladecoder/engine/actions/SetPlayerAction.java index 20d11de32..5e058aea4 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/SetPlayerAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/SetPlayerAction.java @@ -19,6 +19,7 @@ import com.bladecoder.engine.assets.EngineAssetManager; import com.bladecoder.engine.model.BaseActor; import com.bladecoder.engine.model.CharacterActor; +import com.bladecoder.engine.model.Inventory; import com.bladecoder.engine.model.Scene; import com.bladecoder.engine.model.VerbRunner; import com.bladecoder.engine.model.World; @@ -26,35 +27,38 @@ @ActionDescription("Sets the scene player") public class SetPlayerAction implements Action { - @ActionProperty(type = Type.SCENE_CHARACTER_ACTOR, required = true) - @ActionPropertyDescription("The scene player") + @ActionProperty(type = Type.SCENE_CHARACTER_ACTOR) + @ActionPropertyDescription("The scene player") private SceneActorRef actor; - + @ActionProperty - @ActionPropertyDescription("The inventory 'id' for the player. If empty, the inventory will not change.") + @ActionPropertyDescription("The inventory 'id' for the player. If empty, the inventory will not change.") private String inventory; - + private World w; - + @Override public void init(World w) { this.w = w; } @Override - public boolean run(VerbRunner cb) { - Scene s = actor.getScene(w); - - BaseActor a = s.getActor(actor.getActorId(), true); - - s.setPlayer((CharacterActor)a); - - if(inventory != null) { - w.getInventory().dispose(); + public boolean run(VerbRunner cb) { + Scene s = actor == null ? w.getCurrentScene() : actor.getScene(w); + BaseActor a = actor == null || actor.getActorId() == null ? null : s.getActor(actor.getActorId(), true); + + s.setPlayer((CharacterActor) a); + + if (inventory != null && !inventory.equals(w.getCurrentInventory())) { + Inventory old = w.getInventory(); + w.setInventory(inventory); w.getInventory().loadAssets(); EngineAssetManager.getInstance().finishLoading(); w.getInventory().retrieveAssets(); + + old.dispose(); + } return false; diff --git a/blade-engine/src/com/bladecoder/engine/actions/SetStateAction.java b/blade-engine/src/com/bladecoder/engine/actions/SetStateAction.java index 6edaafb3c..9e5c84710 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/SetStateAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/SetStateAction.java @@ -15,6 +15,7 @@ ******************************************************************************/ package com.bladecoder.engine.actions; +import com.bladecoder.engine.actions.Param.Type; import com.bladecoder.engine.model.InteractiveActor; import com.bladecoder.engine.model.Scene; import com.bladecoder.engine.model.VerbRunner; @@ -23,7 +24,7 @@ @ActionDescription(name = "ActorState", value = "Sets the actor state") public class SetStateAction implements Action { - @ActionProperty(required = true) + @ActionProperty(type = Type.SCENE_INTERACTIVE_ACTOR, required = true) @ActionPropertyDescription("The target actor") private SceneActorRef actor; @@ -43,12 +44,6 @@ public boolean run(VerbRunner cb) { final Scene s = actor.getScene(w); String actorId = actor.getActorId(); - if (actorId == null) { - // if called in a scene verb and no actor is specified, set the state of the - // scene - s.setState(state); - return false; - } InteractiveActor a = (InteractiveActor) s.getActor(actorId, true); diff --git a/blade-engine/src/com/bladecoder/engine/actions/SoundAction.java b/blade-engine/src/com/bladecoder/engine/actions/SoundAction.java index 9a25b3ee0..a460a064c 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/SoundAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/SoundAction.java @@ -29,9 +29,9 @@ public class SoundAction implements Action { @ActionPropertyDescription("The actor 'soundId' to play. If empty the current sound will be stopped.") @ActionProperty(type = Type.STRING) private String play; - + private World w; - + @Override public void init(World w) { this.w = w; @@ -39,15 +39,17 @@ public void init(World w) { @Override public boolean run(VerbRunner cb) { - - if(play!= null) { + + if ("$PLAYER".equals(actor)) + actor = w.getCurrentScene().getPlayer().getId(); + + if (play != null) { w.getCurrentScene().getSoundManager().playSound(actor + "_" + play); } else { w.getCurrentScene().getSoundManager().stopCurrentSound(actor); } - + return false; } - } diff --git a/blade-engine/src/com/bladecoder/engine/anim/SpriteAlphaTween.java b/blade-engine/src/com/bladecoder/engine/anim/SpriteAlphaTween.java index 80ed28d85..6d3145d5e 100644 --- a/blade-engine/src/com/bladecoder/engine/anim/SpriteAlphaTween.java +++ b/blade-engine/src/com/bladecoder/engine/anim/SpriteAlphaTween.java @@ -26,23 +26,28 @@ * Tween for SpriteActor alpha animation */ public class SpriteAlphaTween extends Tween { - + private float startAlpha; private float targetAlpha; - + public SpriteAlphaTween() { } - public void start(SpriteActor target, Type repeatType, int count, float tAlpha, float duration, InterpolationMode interpolation, ActionCallback cb) { - + public void start(SpriteActor target, Type repeatType, int count, float tAlpha, float duration, + InterpolationMode interpolation, ActionCallback cb) { + setTarget(target); - - if(target.getTint() == null) + + if (target.getTint() == null) { target.setTint(Color.WHITE.cpy()); - + } else { + // to set the flag dirty + target.setTint(target.getTint()); + } + startAlpha = target.getTint().a; targetAlpha = tAlpha; - + setDuration(duration); setType(repeatType); setCount(count); @@ -51,7 +56,7 @@ public void start(SpriteActor target, Type repeatType, int count, float tAlpha, if (cb != null) { setCb(cb); } - + restart(); } @@ -59,7 +64,7 @@ public void start(SpriteActor target, Type repeatType, int count, float tAlpha, public void updateTarget() { target.getTint().a = startAlpha + getPercent() * (targetAlpha - startAlpha); } - + @Override public void write(Json json) { super.write(json); @@ -70,8 +75,8 @@ public void write(Json json) { @Override public void read(Json json, JsonValue jsonData) { - super.read(json, jsonData); - + super.read(json, jsonData); + startAlpha = json.readValue("startAlpha", float.class, 1.0f, jsonData); targetAlpha = json.readValue("targetAlpha", float.class, 1.0f, jsonData); } diff --git a/blade-engine/src/com/bladecoder/engine/anim/SpriteTintTween.java b/blade-engine/src/com/bladecoder/engine/anim/SpriteTintTween.java index 3bb28ed4c..13253e422 100644 --- a/blade-engine/src/com/bladecoder/engine/anim/SpriteTintTween.java +++ b/blade-engine/src/com/bladecoder/engine/anim/SpriteTintTween.java @@ -26,23 +26,28 @@ * Tween for SpriteActor tint animation */ public class SpriteTintTween extends Tween { - + private Color startColor; private Color targetColor; - + public SpriteTintTween() { } - public void start(SpriteActor target, Type repeatType, int count, Color tColor, float duration, InterpolationMode interpolation, ActionCallback cb) { - + public void start(SpriteActor target, Type repeatType, int count, Color tColor, float duration, + InterpolationMode interpolation, ActionCallback cb) { + setTarget(target); - - if(target.getTint() == null) + + if (target.getTint() == null) { target.setTint(Color.WHITE.cpy()); - + } else { + // to set the flag dirty + target.setTint(target.getTint()); + } + startColor = target.getTint().cpy(); targetColor = tColor.cpy(); - + setDuration(duration); setType(repeatType); setCount(count); @@ -51,19 +56,19 @@ public void start(SpriteActor target, Type repeatType, int count, Color tColor, if (cb != null) { setCb(cb); } - + restart(); } @Override public void updateTarget() { - + target.getTint().a = startColor.a + getPercent() * (targetColor.a - startColor.a); target.getTint().r = startColor.r + getPercent() * (targetColor.r - startColor.r); target.getTint().g = startColor.g + getPercent() * (targetColor.g - startColor.g); target.getTint().b = startColor.b + getPercent() * (targetColor.b - startColor.b); } - + @Override public void write(Json json) { super.write(json); @@ -74,8 +79,8 @@ public void write(Json json) { @Override public void read(Json json, JsonValue jsonData) { - super.read(json, jsonData); - + super.read(json, jsonData); + startColor = json.readValue("startColor", Color.class, Color.WHITE, jsonData); targetColor = json.readValue("targetColor", Color.class, Color.WHITE, jsonData); } diff --git a/blade-engine/src/com/bladecoder/engine/anim/Timers.java b/blade-engine/src/com/bladecoder/engine/anim/Timers.java index b173019ab..e9ac23027 100644 --- a/blade-engine/src/com/bladecoder/engine/anim/Timers.java +++ b/blade-engine/src/com/bladecoder/engine/anim/Timers.java @@ -23,6 +23,8 @@ import com.badlogic.gdx.utils.Json.Serializable; import com.badlogic.gdx.utils.JsonValue; import com.bladecoder.engine.actions.ActionCallback; +import com.bladecoder.engine.model.Scene; +import com.bladecoder.engine.model.World; import com.bladecoder.engine.serialization.ActionCallbackSerializer; import com.bladecoder.engine.serialization.BladeJson; @@ -42,19 +44,19 @@ public void addTimer(float time, ActionCallback cb) { public void clear() { timers.clear(); } - + public boolean isEmpty() { return timers.isEmpty(); } - + public void removeTimerWithCb(ActionCallback cb) { final Iterator it = timers.iterator(); - + while (it.hasNext()) { final Timer t = it.next(); - if(t.cb == cb) { + if (t.cb == cb) { it.remove(); - + return; } } @@ -95,18 +97,22 @@ private static class Timer implements Serializable { public void write(Json json) { json.writeValue("time", time); json.writeValue("currentTime", currentTime); - - if(cb != null) - json.writeValue("cb", ActionCallbackSerializer.find(((BladeJson) json).getWorld(), cb)); + + if (cb != null) { + World w = ((BladeJson) json).getWorld(); + Scene s = ((BladeJson) json).getScene(); + json.writeValue("cb", ActionCallbackSerializer.find(w, s, cb)); + } } @Override public void read(Json json, JsonValue jsonData) { time = json.readValue("time", Float.class, jsonData); currentTime = json.readValue("currentTime", Float.class, jsonData); - + BladeJson bjson = (BladeJson) json; - cb = ActionCallbackSerializer.find(bjson.getWorld(), json.readValue("cb", String.class, jsonData)); + cb = ActionCallbackSerializer.find(bjson.getWorld(), bjson.getScene(), + json.readValue("cb", String.class, jsonData)); } } } diff --git a/blade-engine/src/com/bladecoder/engine/anim/Tween.java b/blade-engine/src/com/bladecoder/engine/anim/Tween.java index 2ef83303a..8b1d4fb5b 100644 --- a/blade-engine/src/com/bladecoder/engine/anim/Tween.java +++ b/blade-engine/src/com/bladecoder/engine/anim/Tween.java @@ -19,6 +19,8 @@ import com.badlogic.gdx.utils.Json.Serializable; import com.badlogic.gdx.utils.JsonValue; import com.bladecoder.engine.actions.ActionCallback; +import com.bladecoder.engine.model.Scene; +import com.bladecoder.engine.model.World; import com.bladecoder.engine.serialization.ActionCallbackSerializer; import com.bladecoder.engine.serialization.BladeJson; import com.bladecoder.engine.util.InterpolationMode; @@ -37,7 +39,7 @@ public enum Type { private int count; private ActionCallback cb; - + protected T target; public Tween() { @@ -65,15 +67,14 @@ public void update(float delta) { reverse = !reverse; } } - + updateTarget(); if (complete) { callCb(); } } - - + /** * Called to update the target property. */ @@ -86,12 +87,11 @@ private void callCb() { tmpcb.resume(); } } - - + public void setTarget(T t) { target = t; } - + public T getTarget() { return target; } @@ -147,7 +147,7 @@ public float getDuration() { public void setDuration(float duration) { this.duration = duration; } - + public void setInterpolation(InterpolationMode i) { interpolation = i; } @@ -199,16 +199,19 @@ public void write(Json json) { json.writeValue("count", count); json.writeValue("interpolation", interpolation); - - if(cb != null) - json.writeValue("cb", ActionCallbackSerializer.find(((BladeJson) json).getWorld(), cb)); + + if (cb != null) { + World w = ((BladeJson) json).getWorld(); + Scene s = ((BladeJson) json).getScene(); + json.writeValue("cb", ActionCallbackSerializer.find(w, s, cb)); + } } @Override public void read(Json json, JsonValue jsonData) { duration = json.readValue("duration", Float.class, jsonData); time = json.readValue("time", Float.class, jsonData); - + reverse = json.readValue("reverse", Boolean.class, jsonData); began = json.readValue("began", Boolean.class, jsonData); complete = json.readValue("complete", Boolean.class, jsonData); @@ -218,6 +221,7 @@ public void read(Json json, JsonValue jsonData) { interpolation = json.readValue("interpolation", InterpolationMode.class, jsonData); BladeJson bjson = (BladeJson) json; - cb = ActionCallbackSerializer.find(bjson.getWorld(), json.readValue("cb", String.class, jsonData)); + cb = ActionCallbackSerializer.find(bjson.getWorld(), bjson.getScene(), + json.readValue("cb", String.class, jsonData)); } } diff --git a/blade-engine/src/com/bladecoder/engine/anim/WalkTween.java b/blade-engine/src/com/bladecoder/engine/anim/WalkTween.java index 6c534c9b5..8ebbb0e7d 100644 --- a/blade-engine/src/com/bladecoder/engine/anim/WalkTween.java +++ b/blade-engine/src/com/bladecoder/engine/anim/WalkTween.java @@ -23,9 +23,13 @@ import com.badlogic.gdx.utils.JsonValue; import com.bladecoder.engine.actions.ActionCallback; import com.bladecoder.engine.assets.EngineAssetManager; +import com.bladecoder.engine.model.AnimationRenderer; import com.bladecoder.engine.model.CharacterActor; +import com.bladecoder.engine.model.Scene; +import com.bladecoder.engine.model.World; import com.bladecoder.engine.serialization.ActionCallbackSerializer; import com.bladecoder.engine.serialization.BladeJson; +import com.bladecoder.engine.util.EngineLogger; import com.bladecoder.engine.util.InterpolationMode; /** @@ -60,34 +64,42 @@ private void walkToNextStep(CharacterActor target) { Vector2 p0 = walkingPath.get(currentStep); Vector2 pf = walkingPath.get(currentStep + 1); - target.startWalkAnim(p0, pf); - float s0 = 1.0f; float sf = 1.0f; - + if (target.getFakeDepth()) { s0 = target.getScene().getFakeDepthScale(p0.y); sf = target.getScene().getFakeDepthScale(pf.y); } - // float segmentDuration = p0.dst(pf) - // / (EngineAssetManager.getInstance().getScale() * speed); + float sdiff = Math.abs(s0 - sf); + if (sdiff > .05f) { + // cut the path in two parts if the difference in scale is big + Vector2 pi = new Vector2((pf.x + p0.x) / 2, (pf.y + p0.y) / 2); - // t = dst/((vf+v0)/2) - float segmentDuration = p0.dst(pf) / (EngineAssetManager.getInstance().getScale() * speed * (s0 + sf) / 2); + if (EngineLogger.debugMode()) { + String debugText = String.format( + "WalkTween insert point: sdiff=%.2f, p0=(%.0f,%.0f), pf=(%.0f,%.0f), pi=(%.0f,%.0f)", sdiff, + p0.x, p0.y, pf.x, pf.y, pi.x, pi.y); - segmentDuration *= (s0 > sf ? s0 / sf : sf / s0); + EngineLogger.debug(debugText); + } - InterpolationMode i = InterpolationMode.LINEAR; + walkingPath.add(currentStep + 1, pi); - if (Math.abs(s0 - sf) > .25) - i = s0 > sf ? InterpolationMode.POW2OUT : InterpolationMode.POW2IN; + walkToNextStep(target); + return; + } - if (currentStep == walkingPath.size() - 2 && walkCb != null) { - start(target, Type.NO_REPEAT, 1, pf.x, pf.y, segmentDuration, InterpolationMode.LINEAR, i, walkCb); - } else { - start(target, Type.NO_REPEAT, 1, pf.x, pf.y, segmentDuration, InterpolationMode.LINEAR, i, null); + if (currentStep == 0 || ((AnimationRenderer) target.getRenderer()).changeDir(p0, pf)) { + target.startWalkAnim(p0, pf); } + + // t = dst/((vf+v0)/2) + float segmentDuration = p0.dst(pf) / (EngineAssetManager.getInstance().getScale() * speed * (s0 + sf) / 2); + + start(target, Type.NO_REPEAT, 1, pf.x, pf.y, segmentDuration, InterpolationMode.LINEAR, + InterpolationMode.LINEAR, currentStep == walkingPath.size() - 2 ? walkCb : null); } private void segmentEnded(CharacterActor target) { @@ -132,8 +144,11 @@ public void write(Json json) { json.writeValue("currentStep", currentStep); json.writeValue("speed", speed); - if(walkCb != null) - json.writeValue("walkCb", ActionCallbackSerializer.find(((BladeJson) json).getWorld(), walkCb)); + if (walkCb != null) { + World w = ((BladeJson) json).getWorld(); + Scene s = ((BladeJson) json).getScene(); + json.writeValue("walkCb", ActionCallbackSerializer.find(w, s, walkCb)); + } } @SuppressWarnings("unchecked") @@ -145,7 +160,8 @@ public void read(Json json, JsonValue jsonData) { currentStep = json.readValue("currentStep", Integer.class, jsonData); speed = json.readValue("speed", Float.class, jsonData); - walkCb = ActionCallbackSerializer.find(((BladeJson) json).getWorld(), - json.readValue("walkCb", String.class, jsonData)); + World w = ((BladeJson) json).getWorld(); + Scene s = ((BladeJson) json).getScene(); + walkCb = ActionCallbackSerializer.find(w, s, json.readValue("walkCb", String.class, jsonData)); } } diff --git a/blade-engine/src/com/bladecoder/engine/assets/EngineAssetManager.java b/blade-engine/src/com/bladecoder/engine/assets/EngineAssetManager.java index 289489be7..a33b1c4cd 100644 --- a/blade-engine/src/com/bladecoder/engine/assets/EngineAssetManager.java +++ b/blade-engine/src/com/bladecoder/engine/assets/EngineAssetManager.java @@ -15,6 +15,7 @@ ******************************************************************************/ package com.bladecoder.engine.assets; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URL; @@ -56,8 +57,7 @@ public class EngineAssetManager extends AssetManager { public static final String WORLD_FILENAME = "world"; - public static final String DESKTOP_PREFS_DIR = "BladeEngine"; - public static final String NOT_DESKTOP_PREFS_DIR = "data/"; + public static final String DEFAULT_USER_FOLDER = "BladeEngine/"; public static final String ATLASES_DIR = "atlases/"; public static final String MODEL_DIR = "model/"; @@ -87,6 +87,8 @@ public class EngineAssetManager extends AssetManager { private EngineResolutionFileResolver resResolver; + private String userFolder = DEFAULT_USER_FOLDER; + protected EngineAssetManager() { this(new InternalFileHandleResolver()); // getLogger().setLevel(Application.LOG_DEBUG); @@ -131,14 +133,17 @@ public static EngineAssetManager getInstance() { return instance; } + public void setUserFolder(String f) { + userFolder = f; + } + /** * Creates a EngineAssetManager instance for edition. That is: * - * - Puts a PathResolver to locate the assets through an absolute path - - * Puts assets scale to "1" + * - Puts a PathResolver to locate the assets through an absolute path - Puts + * assets scale to "1" * - * @param base - * is the project base folder + * @param base is the project base folder */ public static void createEditInstance(String base) { if (instance != null) @@ -152,14 +157,16 @@ public static void createEditInstance(String base) { /** * All assets will be searched in the selected folder. * - * @param base - * The asset base folder + * @param base The asset base folder */ public static void setAssetFolder(String base) { if (instance != null) instance.dispose(); - instance = new EngineAssetManager(new InternalFolderResolver(base)); + if (base != null) + instance = new EngineAssetManager(new InternalFolderResolver(base)); + else + instance = new EngineAssetManager(new InternalFileHandleResolver()); } public void forceResolution(String suffix) { @@ -194,8 +201,7 @@ public FileHandle getModelFile(String filename) { } /** - * Returns a file in the asset directory SEARCHING in the resolution - * directories + * Returns a file in the asset directory SEARCHING in the resolution directories */ public FileHandle getResAsset(String filename) { return resResolver.resolve(filename); @@ -256,6 +262,7 @@ public Texture getTexture(String filename) { return get(filename, Texture.class); } + @Override public void dispose() { super.dispose(); instance = null; @@ -277,13 +284,17 @@ public String checkIOSSoundName(String filename) { return filename; } - public void loadMusic(String filename) { + public void loadMusic(String filename) throws FileNotFoundException { String n = checkIOSSoundName(MUSIC_DIR + filename); if (n == null) return; + if (!FileUtils.exists(EngineAssetManager.getInstance().getAsset(n))) { + throw new FileNotFoundException(n); + } + load(n, Music.class); } @@ -306,12 +317,16 @@ public Music getMusic(String filename) { return get(n, Music.class); } - public void loadSound(String filename) { + public void loadSound(String filename) throws FileNotFoundException { String n = checkIOSSoundName(SOUND_DIR + filename); if (n == null) return; + if (!FileUtils.exists(EngineAssetManager.getInstance().getAsset(n))) { + throw new FileNotFoundException(n); + } + load(n, Sound.class); } @@ -356,20 +371,20 @@ public boolean assetExists(String filename) { } private Resolution[] getResolutions(FileHandleResolver resolver, int worldWidth, int worldHeight) { - ArrayList rl = new ArrayList(); + ArrayList rl = new ArrayList<>(); String list[] = null; - - String configRes = Config.getProperty(Config.RESOLUTIONS, null); - - if(configRes != null) { + + String configRes = Config.getInstance().getProperty(Config.RESOLUTIONS, null); + + if (configRes != null) { list = configRes.split(","); } else { list = listAssetFiles("ui"); } - + for (String name : list) { - + try { float scale = Float.parseFloat(name); @@ -384,6 +399,7 @@ private Resolution[] getResolutions(FileHandleResolver resolver, int worldWidth, } Collections.sort(rl, new Comparator() { + @Override public int compare(Resolution a, Resolution b) { return a.portraitWidth - b.portraitWidth; } @@ -443,8 +459,8 @@ public String[] listAssetFiles(String base) { private String[] getFilesFromJar(String base) { URL dirURL = EngineAssetManager.class.getResource(base); - Set result = new HashSet(); // avoid duplicates in case - // it is a subdirectory + Set result = new HashSet<>(); // avoid duplicates in case + // it is a subdirectory if (dirURL.getProtocol().equals("jar")) { /* A JAR path */ @@ -488,50 +504,27 @@ private String[] getFilesFromJar(String base) { } public FileHandle getUserFile(String filename) { - FileHandle file = null; - - if (Gdx.app.getType() == ApplicationType.Desktop || Gdx.app.getType() == ApplicationType.Applet) { - String dir = Config.getProperty(Config.TITLE_PROP, DESKTOP_PREFS_DIR); - dir.replace(" ", ""); - - StringBuilder sb = new StringBuilder(); - sb.append(".").append(dir).append("/").append(filename); - - if (System.getProperty("os.name").toLowerCase().contains("mac") - && System.getenv("HOME").contains("Containers")) { - - file = Gdx.files.absolute(System.getenv("HOME") + "/" + sb.toString()); - } else { - - file = Gdx.files.external(sb.toString()); - } - } else { - file = Gdx.files.local(NOT_DESKTOP_PREFS_DIR + filename); - } - - return file; + return getUserFolder().child(filename); } public FileHandle getUserFolder() { FileHandle file = null; - if (Gdx.app.getType() == ApplicationType.Desktop || Gdx.app.getType() == ApplicationType.Applet) { - String dir = Config.getProperty(Config.TITLE_PROP, DESKTOP_PREFS_DIR); - dir.replace(" ", ""); + if (Gdx.app.getType() == ApplicationType.Desktop) { + + StringBuilder sb = new StringBuilder(); - StringBuilder sb = new StringBuilder("."); - if (System.getProperty("os.name").toLowerCase().contains("mac") && System.getenv("HOME").contains("Containers")) { - file = Gdx.files.absolute(System.getenv("HOME") + "/" + sb.append(dir).toString()); + file = Gdx.files.absolute(System.getenv("HOME") + "/" + sb.append(userFolder).toString()); } else { - file = Gdx.files.external(sb.append(dir).toString()); + file = Gdx.files.external(sb.append(userFolder).toString()); } - + } else { - file = Gdx.files.local(NOT_DESKTOP_PREFS_DIR); + file = Gdx.files.local(userFolder); } return file; diff --git a/blade-engine/src/com/bladecoder/engine/i18n/I18N.java b/blade-engine/src/com/bladecoder/engine/i18n/I18N.java index df10bb112..b7757facf 100644 --- a/blade-engine/src/com/bladecoder/engine/i18n/I18N.java +++ b/blade-engine/src/com/bladecoder/engine/i18n/I18N.java @@ -25,47 +25,47 @@ public class I18N { public static final String ENCODING = "UTF-8"; // public static final String ENCODING = "ISO-8859-1"; - private static ResourceBundle i18nWorld; - private static ResourceBundle i18nChapter; - private static Locale locale = Locale.getDefault(); - - private static String i18nChapterFilename = null; - private static String i18nWorldFilename = null; - - public static void loadChapter(String i18nChapterFilename) { + private ResourceBundle i18nWorld; + private ResourceBundle i18nChapter; + private Locale locale = Locale.getDefault(); + + private String i18nChapterFilename = null; + private String i18nWorldFilename = null; + + public void loadChapter(String i18nChapterFilename) { try { i18nChapter = getBundle(i18nChapterFilename, false); - I18N.i18nChapterFilename = i18nChapterFilename; + this.i18nChapterFilename = i18nChapterFilename; } catch (Exception e) { EngineLogger.error("ERROR LOADING BUNDLE: " + i18nChapterFilename); } } - public static void loadWorld(String i18nWorldFilename) { + public void loadWorld(String i18nWorldFilename) { try { i18nWorld = getBundle(i18nWorldFilename, true); - I18N.i18nWorldFilename = i18nWorldFilename; + this.i18nWorldFilename = i18nWorldFilename; } catch (Exception e) { EngineLogger.error("ERROR LOADING BUNDLE: " + i18nWorldFilename); } } - - public static ResourceBundle getBundle(String filename, boolean clearCache) { + + public ResourceBundle getBundle(String filename, boolean clearCache) { ResourceBundle rb = null; - + try { - if(clearCache) + if (clearCache) ResourceBundle.clearCache(); - + rb = ResourceBundle.getBundle(filename, locale, new I18NControl(ENCODING)); } catch (Exception e) { EngineLogger.error("ERROR LOADING BUNDLE: " + filename); } - + return rb; } - public static void setLocale(Locale l) { + public void setLocale(Locale l) { locale = l; // RELOAD TRANSLATIONS @@ -78,7 +78,7 @@ public static void setLocale(Locale l) { } } - public static String getString(String key) { + public String getString(String key) { try { return i18nChapter.getString(key); } catch (Exception e) { @@ -89,9 +89,9 @@ public static String getString(String key) { return key; } } - } - - public static Locale getCurrentLocale() { + } + + public Locale getCurrentLocale() { return locale; } } diff --git a/blade-engine/src/com/bladecoder/engine/ink/ExternalFunctions.java b/blade-engine/src/com/bladecoder/engine/ink/ExternalFunctions.java index 8e30fbcbf..c923f949e 100644 --- a/blade-engine/src/com/bladecoder/engine/ink/ExternalFunctions.java +++ b/blade-engine/src/com/bladecoder/engine/ink/ExternalFunctions.java @@ -1,29 +1,147 @@ package com.bladecoder.engine.ink; +import com.bladecoder.engine.actions.SceneActorRef; +import com.bladecoder.engine.model.InteractiveActor; +import com.bladecoder.engine.model.Inventory; +import com.bladecoder.engine.model.Scene; import com.bladecoder.engine.model.World; -import com.bladecoder.ink.runtime.Story.ExternalFunction; +import com.bladecoder.engine.util.EngineLogger; +import com.bladecoder.ink.runtime.Story; +import com.bladecoder.ink.runtime.Story.ExternalFunction0; +import com.bladecoder.ink.runtime.Story.ExternalFunction1; +import com.bladecoder.ink.runtime.Story.ExternalFunction2; public class ExternalFunctions { - private InkManager inkManager; + public static void bindExternalFunctions(final World w, Story story) throws Exception { - public ExternalFunctions() { - } + story.bindExternalFunction("inInventory", new ExternalFunction1() { + + @Override + public Boolean call(String actor) throws Exception { + if (actor.charAt(0) == '>') + actor = actor.substring(1); + + return w.getInventory().get(actor) != null; + } + }); + + story.bindExternalFunction("getActorState", new ExternalFunction1() { + + @Override + public String call(String act) throws Exception { + if (act.charAt(0) == '>') + act = act.substring(1); + + SceneActorRef actor = new SceneActorRef(act); + final Scene s = actor.getScene(w); + + String actorId = actor.getActorId(); + + InteractiveActor a = (InteractiveActor) s.getActor(actorId, true); - public void bindExternalFunctions(final World w, InkManager ink) throws Exception { + if (a == null) { + EngineLogger.error("getActorState - Actor not found: " + actorId); + return ""; + } - this.inkManager = ink; + return a.getState() == null ? "" : a.getState(); + } + }); - inkManager.getStory().bindExternalFunction("inInventory", new ExternalFunction() { + story.bindExternalFunction("getSceneState", new ExternalFunction1() { @Override - public Object call(Object[] args) throws Exception { - String actor = args[0].toString(); + public String call(String scene) throws Exception { + if (scene.charAt(0) == '>') + scene = scene.substring(1); + + final Scene s = w.getScene(scene); + + if (s == null) { + EngineLogger.error("getSceneState - Scene not found: " + scene); + return ""; + } + + return s.getState() == null ? "" : s.getState(); + } + }); + + story.bindExternalFunction("getPlayer", new ExternalFunction0() { + @Override + public String call() throws Exception { + return w.getCurrentScene().getPlayer().getId(); + } + }); + + story.bindExternalFunction("inInventory2", new ExternalFunction2() { + + @Override + public Boolean call(String actor, String inventory) throws Exception { if (actor.charAt(0) == '>') actor = actor.substring(1); - return w.getInventory().get(actor) != null; + Inventory inv = w.getInventories().get(inventory); + + if (inv == null) { + EngineLogger.debug("InkExternalFunction::inInventory2: Inventory not found: " + inventory); + + return false; + } + + return inv.get(actor) != null; + } + }); + + story.bindExternalFunction("getCurrentScene", new ExternalFunction0() { + + @Override + public String call() throws Exception { + return w.getCurrentScene().getId(); + } + }); + + story.bindExternalFunction("isDebug", new ExternalFunction0() { + + @Override + public Boolean call() throws Exception { + return EngineLogger.debugMode(); + } + }); + + story.bindExternalFunction("getProperty", new ExternalFunction1() { + + @Override + public String call(String p) throws Exception { + if (p.charAt(0) == '>') + p = p.substring(1); + + String v = w.getCustomProperty(p); + return v == null ? "" : v; + } + }); + + story.bindExternalFunction("isVisible", new ExternalFunction1() { + + @Override + public Boolean call(String act) throws Exception { + if (act.charAt(0) == '>') + act = act.substring(1); + + SceneActorRef actor = new SceneActorRef(act); + final Scene s = actor.getScene(w); + + String actorId = actor.getActorId(); + + InteractiveActor a = (InteractiveActor) s.getActor(actorId, true); + + if (a == null) { + EngineLogger.error("getActorState - Actor not found: " + actorId); + return false; + } + + return a.isVisible(); } }); } diff --git a/blade-engine/src/com/bladecoder/engine/ink/InkManager.java b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java index f08996b17..5212ad315 100644 --- a/blade-engine/src/com/bladecoder/engine/ink/InkManager.java +++ b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java @@ -33,7 +33,8 @@ import com.bladecoder.ink.runtime.ListDefinition; import com.bladecoder.ink.runtime.Story; -public class InkManager implements VerbRunner, Serializable { +public class InkManager implements Serializable { + public final static int KEY_SIZE = 10; public final static char NAME_VALUE_TAG_SEPARATOR = ':'; public final static char NAME_VALUE_PARAM_SEPARATOR = '='; private final static String PARAM_SEPARATOR = ","; @@ -42,7 +43,6 @@ public class InkManager implements VerbRunner, Serializable { private ResourceBundle i18n; private Story story = null; - private ExternalFunctions externalFunctions; private ActionCallback cb; @@ -50,22 +50,18 @@ public class InkManager implements VerbRunner, Serializable { // the verbCallbacks may not exist. So, we search the Cb lazily when needed. private String sCb; - private ArrayList actions; - private boolean wasInCutmode; private String storyName; - private int ip = -1; - private final World w; + private InkVerbRunner inkVerbRunner = new InkVerbRunner(); + private Thread loaderThread; public InkManager(World w) { this.w = w; - externalFunctions = new ExternalFunctions(); - actions = new ArrayList<>(); } public void newStory(final String name) throws Exception { @@ -82,7 +78,7 @@ private void loadStory(String name) { String json = getJsonString(asset.read()); story = new Story(json); - externalFunctions.bindExternalFunctions(w, this); + ExternalFunctions.bindExternalFunctions(w, story); storyName = name; @@ -110,7 +106,7 @@ private void loadStoryState(String stateString) { public void loadI18NBundle() { if (getStoryName() != null && EngineAssetManager.getInstance().getModelFile(storyName + "-ink.properties").exists()) - i18n = I18N.getBundle(EngineAssetManager.MODEL_DIR + storyName + "-ink", true); + i18n = w.getI18N().getBundle(EngineAssetManager.MODEL_DIR + storyName + "-ink", true); } public String translateLine(String line) { @@ -124,7 +120,13 @@ public String translateLine(String line) { for (String k : keys) { try { - translated += i18n.getString(k); + // some untranslated words may follow the key + String k2 = k.substring(0, KEY_SIZE); + translated += i18n.getString(k2); + if (k.length() > KEY_SIZE) { + String trailing = k.substring(KEY_SIZE); + translated += trailing; + } } catch (Exception e) { EngineLogger.error("MISSING TRANSLATION KEY: " + key); return key; @@ -150,7 +152,7 @@ public boolean compareVariable(String name, String value) { if (story.getVariablesState().get(name) instanceof InkList) { return ((InkList) story.getVariablesState().get(name)).ContainsItemNamed(value); } else { - return story.getVariablesState().get(name).toString().equals(value); + return story.getVariablesState().get(name).toString().equals(value == null ? "" : value); } } @@ -183,7 +185,11 @@ private void continueMaximally() { waitIfNotLoaded(); String line = null; - actions.clear(); + + // We create a new InkVerbRunner every ink loop to avoid pending cb.resume() to + // execute in the new actions + inkVerbRunner.cancel(); + inkVerbRunner = new InkVerbRunner(); HashMap currentLineParams = new HashMap<>(); @@ -221,8 +227,8 @@ private void continueMaximally() { } - if (actions.size() > 0) { - run(null, null); + if (inkVerbRunner.getActions().size() > 0) { + inkVerbRunner.run(null, null); } else { if (hasChoices()) { @@ -231,11 +237,12 @@ private void continueMaximally() { w.getListener().dialogOptions(); } else if (cb != null || sCb != null) { if (cb == null) { - cb = ActionCallbackSerializer.find(w, sCb); + cb = ActionCallbackSerializer.find(w, w.getCurrentScene(), sCb); } ActionCallback tmpcb = cb; cb = null; + sCb = null; tmpcb.resume(); } } @@ -282,31 +289,34 @@ private void processCommand(HashMap params, String line) { processParams(Arrays.asList(commandParams), params); } - if ("leave".equals(commandName)) { + if ("LeaveNow".equals(commandName)) { boolean init = true; + String initVerb = null; if (params.get("init") != null) init = Boolean.parseBoolean(params.get("init")); - w.setCurrentScene(params.get("scene"), init); - } else { - - // for backward compatibility - if ("action".equals(commandName)) { - commandName = commandParams[0].trim(); - params.remove(commandName); - } + if (params.get("initVerb") != null) + initVerb = params.get("initVerb"); + w.setCurrentScene(params.get("scene"), init, initVerb); + } else { // Some preliminar validation to see if it's an action - if (commandName.length() > 0 && Character.isUpperCase(commandName.charAt(0))) { + if (commandName.length() > 0) { // Try to create action by default Action action; try { - action = ActionFactory.createByClass("com.bladecoder.engine.actions." + commandName + "Action", - params); + + Class c = ActionFactory.getClassTags().get(commandName); + + if (c == null && commandName.indexOf('.') == -1) { + commandName = "com.bladecoder.engine.actions." + commandName + "Action"; + } + + action = ActionFactory.create(commandName, params); action.init(w); - actions.add(action); + inkVerbRunner.getActions().add(action); } catch (ClassNotFoundException | ReflectionException e) { EngineLogger.error(e.getMessage(), e); } @@ -331,8 +341,6 @@ private void processTextLine(HashMap params, String line) { } if (!params.containsKey("actor") && w.getCurrentScene().getPlayer() != null) { - // params.put("actor", Scene.VAR_PLAYER); - if (!params.containsKey("type")) { params.put("type", Type.SUBTITLE.toString()); } @@ -348,44 +356,21 @@ private void processTextLine(HashMap params, String line) { Action action = null; if (!params.containsKey("actor")) { - action = ActionFactory.createByClass("com.bladecoder.engine.actions.TextAction", params); + action = ActionFactory.create("Text", params); } else { - action = ActionFactory.createByClass("com.bladecoder.engine.actions.SayAction", params); + action = ActionFactory.create("Say", params); } action.init(w); - actions.add(action); + inkVerbRunner.getActions().add(action); } catch (ClassNotFoundException | ReflectionException e) { EngineLogger.error(e.getMessage(), e); } } - private void nextStep() { - if (ip < 0) { - continueMaximally(); - } else { - boolean stop = false; - - while (ip < actions.size() && !stop) { - Action a = actions.get(ip); - - try { - if (a.run(this)) - stop = true; - else - ip++; - } catch (Exception e) { - EngineLogger.error("EXCEPTION EXECUTING ACTION: " + a.getClass().getSimpleName(), e); - ip++; - } - } - - if (ip >= actions.size() && !stop) - continueMaximally(); - } - } - public Story getStory() { + waitIfNotLoaded(); + return story; } @@ -398,6 +383,7 @@ public void runPath(String path, ActionCallback cb) throws Exception { } this.cb = cb; + sCb = null; story.choosePathString(path); continueMaximally(); @@ -406,7 +392,7 @@ public void runPath(String path, ActionCallback cb) throws Exception { public boolean hasChoices() { waitIfNotLoaded(); - return (story != null && actions.size() == 0 && story.getCurrentChoices().size() > 0); + return (story != null && inkVerbRunner.getActions().size() == 0 && story.getCurrentChoices().size() > 0); } public List getChoices() { @@ -456,12 +442,6 @@ private String getJsonString(InputStream is) throws IOException { } } - @Override - public void resume() { - ip++; - nextStep(); - } - public void selectChoice(int i) { w.setCutMode(wasInCutmode); @@ -473,44 +453,6 @@ public void selectChoice(int i) { } } - @Override - public ArrayList getActions() { - return actions; - } - - @Override - public void run(String currentTarget, ActionCallback cb) { - ip = 0; - nextStep(); - } - - @Override - public int getIP() { - return ip; - } - - @Override - public void setIP(int ip) { - this.ip = ip; - } - - @Override - public void cancel() { - ArrayList actions = getActions(); - - for (Action c : actions) { - if (c instanceof VerbRunner) - ((VerbRunner) c).cancel(); - } - - ip = actions.size(); - } - - @Override - public String getCurrentTarget() { - return null; - } - public String getStoryName() { return storyName; } @@ -545,6 +487,10 @@ public void run() { loaderThread.start(); } + public InkVerbRunner getVerbRunner() { + return inkVerbRunner; + } + @Override public void write(Json json) { BladeJson bjson = (BladeJson) json; @@ -556,22 +502,22 @@ public void write(Json json) { json.writeValue("wasInCutmode", wasInCutmode); if (cb == null && sCb != null) - cb = ActionCallbackSerializer.find(w, sCb); + cb = ActionCallbackSerializer.find(w, w.getCurrentScene(), sCb); if (cb != null) - json.writeValue("cb", ActionCallbackSerializer.find(w, cb)); + json.writeValue("cb", ActionCallbackSerializer.find(w, w.getCurrentScene(), cb)); // SAVE ACTIONS json.writeArrayStart("actions"); - for (Action a : actions) { + for (Action a : inkVerbRunner.getActions()) { ActionUtils.writeJson(a, json); } json.writeArrayEnd(); - json.writeValue("ip", ip); + json.writeValue("ip", inkVerbRunner.getIP()); json.writeArrayStart("actionsSer"); - for (Action a : actions) { + for (Action a : inkVerbRunner.getActions()) { if (a instanceof Serializable) { json.writeObjectStart(); ((Serializable) a).write(json); @@ -613,22 +559,24 @@ public void read(Json json, JsonValue jsonData) { sCb = json.readValue("cb", String.class, jsonData); // READ ACTIONS - actions.clear(); JsonValue actionsValue = jsonData.get("actions"); + + inkVerbRunner = new InkVerbRunner(); + for (int i = 0; i < actionsValue.size; i++) { JsonValue aValue = actionsValue.get(i); Action a = ActionUtils.readJson(w, json, aValue); - actions.add(a); + inkVerbRunner.getActions().add(a); } - ip = json.readValue("ip", Integer.class, jsonData); + inkVerbRunner.setIP(json.readValue("ip", Integer.class, jsonData)); actionsValue = jsonData.get("actionsSer"); int i = 0; - for (Action a : actions) { + for (Action a : inkVerbRunner.getActions()) { if (a instanceof Serializable && i < actionsValue.size) { if (actionsValue.get(i) == null) break; @@ -645,4 +593,84 @@ public void read(Json json, JsonValue jsonData) { } } } + + public final class InkVerbRunner implements VerbRunner { + + private ArrayList actions; + private int ip = -1; + private boolean cancelled = false; + + public InkVerbRunner() { + actions = new ArrayList<>(); + } + + @Override + public void resume() { + ip++; + + nextStep(); + } + + @Override + public ArrayList getActions() { + return actions; + } + + @Override + public void run(String currentTarget, ActionCallback cb) { + ip = 0; + nextStep(); + } + + @Override + public int getIP() { + return ip; + } + + @Override + public void setIP(int ip) { + this.ip = ip; + } + + @Override + public void cancel() { + cancelled = true; + ip = actions.size(); + } + + @Override + public String getCurrentTarget() { + return null; + } + + private void nextStep() { + if (cancelled) + return; + + if (ip < 0) { + continueMaximally(); + } else { + boolean stop = false; + + while (ip < actions.size() && !stop && !cancelled) { + Action a = actions.get(ip); + + try { + if (a.run(this)) + stop = true; + else + ip++; + } catch (Exception e) { + EngineLogger.error("EXCEPTION EXECUTING ACTION: InkManager - " + ip + " - " + + a.getClass().getSimpleName() + " - " + e.getMessage(), e); + ip++; + } + } + + if (ip >= actions.size() && !stop) + continueMaximally(); + } + } + + } } diff --git a/blade-engine/src/com/bladecoder/engine/model/ActorRenderer.java b/blade-engine/src/com/bladecoder/engine/model/ActorRenderer.java index 2f3ad0e30..d1f51c307 100644 --- a/blade-engine/src/com/bladecoder/engine/model/ActorRenderer.java +++ b/blade-engine/src/com/bladecoder/engine/model/ActorRenderer.java @@ -24,19 +24,24 @@ public interface ActorRenderer extends Serializable, AssetConsumer { public void update(float delta); + public void draw(SpriteBatch batch, float x, float y, float scaleX, float scaleY, float rotation, Color tint); - + public float getWidth(); + public float getHeight(); - + public int getOrgAlign(); + public void setOrgAlign(int align); - + /** * Compute the bbox based in the size of the animation/sprite. T * - * @param bbox The polygon to update. It will be updated when an animation starts/finishs. + * @param bbox The polygon to update. It will be updated when an animation + * starts/finishes. */ public void updateBboxFromRenderer(Polygon bbox); -} + public void setWorld(World world); +} diff --git a/blade-engine/src/com/bladecoder/engine/model/AnimationRenderer.java b/blade-engine/src/com/bladecoder/engine/model/AnimationRenderer.java index f79688fe2..931c001c4 100644 --- a/blade-engine/src/com/bladecoder/engine/model/AnimationRenderer.java +++ b/blade-engine/src/com/bladecoder/engine/model/AnimationRenderer.java @@ -16,6 +16,8 @@ package com.bladecoder.engine.model; import java.util.HashMap; +import java.util.SortedMap; +import java.util.TreeMap; import com.badlogic.gdx.math.Polygon; import com.badlogic.gdx.math.Vector2; @@ -41,7 +43,7 @@ public abstract class AnimationRenderer implements ActorRenderer { private final static float DEFAULT_DIM = 200; - protected HashMap fanims = new HashMap(); + protected HashMap fanims = new HashMap<>(); /** Starts this anim the first time that the scene is loaded */ protected String initAnimation; @@ -51,9 +53,11 @@ public abstract class AnimationRenderer implements ActorRenderer { protected CacheEntry currentSource; protected boolean flipX; - protected final HashMap sourceCache = new HashMap(); + protected final HashMap sourceCache = new HashMap<>(); protected Polygon bbox; + protected World world; + public class CacheEntry { public int refCounter; } @@ -120,6 +124,29 @@ public String getCurrentAnimationId() { } + /** + * Checks if the character faces the target point to avoid changing the + * animation. + */ + public boolean changeDir(Vector2 p0, Vector2 pf) { + int idx = getCurrentAnimationId().indexOf('.'); + + if (idx == -1) { + return true; + } + + String id = getCurrentAnimationId().substring(0, idx); + + String directionString = getDirectionString(p0, pf, getDirs(id, fanims)); + + if (getCurrentAnimationId().substring(idx + 1).equals(directionString)) { + return false; + } + + return true; + } + + @Override public String toString() { StringBuilder sb = new StringBuilder(super.toString()); @@ -137,6 +164,7 @@ public String toString() { return sb.toString(); } + @Override public void updateBboxFromRenderer(Polygon bbox) { this.bbox = bbox; computeBbox(); @@ -350,8 +378,7 @@ protected String getDirectionString(Vector2 p0, Vector2 pf, int numDirs) { * FRONT, BACK) 2 -> when 2 dir animation mode (RIGHT, LEFT) 0 -> when no dirs * availables for the base animation -1 -> when base animation doesn't exists * - * @param base - * Base animation + * @param base Base animation * @param fanims * @return -1, 0, 2, 4 or 8 */ @@ -379,7 +406,10 @@ public void write(Json json) { BladeJson bjson = (BladeJson) json; if (bjson.getMode() == Mode.MODEL) { - json.writeValue("fanims", fanims, HashMap.class, null); + SortedMap sortedAnims = new TreeMap<>(); + sortedAnims.putAll(fanims); + + json.writeValue("fanims", sortedAnims, sortedAnims.getClass(), null); json.writeValue("initAnimation", initAnimation); json.writeValue("orgAlign", orgAlign); @@ -417,4 +447,9 @@ public void read(Json json, JsonValue jsonData) { flipX = json.readValue("flipX", Boolean.class, jsonData); } } + + @Override + public void setWorld(World world) { + this.world = world; + } } diff --git a/blade-engine/src/com/bladecoder/engine/model/AtlasRenderer.java b/blade-engine/src/com/bladecoder/engine/model/AtlasRenderer.java index 8d53f31bf..6d01e11bd 100644 --- a/blade-engine/src/com/bladecoder/engine/model/AtlasRenderer.java +++ b/blade-engine/src/com/bladecoder/engine/model/AtlasRenderer.java @@ -55,7 +55,7 @@ public String[] getInternalAnimations(AnimationDesc anim) { TextureAtlas atlas = EngineAssetManager.getInstance().getTextureAtlas(anim.source); Array animations = atlas.getRegions(); - ArrayList l = new ArrayList(); + ArrayList l = new ArrayList<>(); for (int i = 0; i < animations.size; i++) { AtlasRegion a = animations.get(i); @@ -70,7 +70,7 @@ public String[] getInternalAnimations(AnimationDesc anim) { public void update(float delta) { if (faTween != null) { faTween.update(delta); - if (faTween.isComplete()) { + if (faTween != null && faTween.isComplete()) { faTween = null; computeBbox(); } diff --git a/blade-engine/src/com/bladecoder/engine/model/BaseActor.java b/blade-engine/src/com/bladecoder/engine/model/BaseActor.java index d083e7bfe..ce4d692f9 100644 --- a/blade-engine/src/com/bladecoder/engine/model/BaseActor.java +++ b/blade-engine/src/com/bladecoder/engine/model/BaseActor.java @@ -32,9 +32,10 @@ abstract public class BaseActor implements Serializable { protected String id; protected Scene scene = null; - protected boolean visible = true; - protected final Polygon bbox = new Polygon(); + private boolean visible = true; + private final Polygon bbox = new Polygon(); private String initScene; + protected long dirtyProps = 0L; public String getId() { return id; @@ -58,6 +59,7 @@ public boolean isVisible() { public void setVisible(boolean visible) { this.visible = visible; + setDirtyProp(DirtyProps.VISIBLE); } public void setScene(Scene s) { @@ -70,6 +72,14 @@ public Scene getScene() { abstract public void update(float delta); + protected void setDirtyProp(DirtyProps dp) { + dirtyProps |= (1L << dp.ordinal()); + } + + protected boolean isDirty(DirtyProps dp) { + return (dirtyProps & (1L << dp.ordinal())) != 0; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -92,7 +102,10 @@ public float getY() { } public void setPosition(float x, float y) { - bbox.setPosition(x, y); + if (x != bbox.getX() || y != bbox.getY()) { + bbox.setPosition(x, y); + setDirtyProp(DirtyProps.POS); + } } public String getInitScene() { @@ -110,14 +123,18 @@ public void write(Json json) { json.writeValue("id", id); json.writeValue("bbox", bbox.getVertices()); } else { - + if (dirtyProps != 0L) + json.writeValue("dirtyProps", dirtyProps); } - float worldScale = EngineAssetManager.getInstance().getScale(); - Vector2 scaledPos = new Vector2(bbox.getX() / worldScale, bbox.getY() / worldScale); - json.writeValue("pos", scaledPos); + if (bjson.getMode() == Mode.MODEL || isDirty(DirtyProps.POS)) { + float worldScale = EngineAssetManager.getInstance().getScale(); + Vector2 scaledPos = new Vector2(bbox.getX() / worldScale, bbox.getY() / worldScale); + json.writeValue("pos", scaledPos); + } - json.writeValue("visible", visible); + if (bjson.getMode() == Mode.MODEL || isDirty(DirtyProps.VISIBLE)) + json.writeValue("visible", visible); } @Override @@ -130,8 +147,6 @@ public void read(Json json, JsonValue jsonData) { if (verts.length > 0) bbox.setVertices(verts); - } else { - } Vector2 pos = json.readValue("pos", Vector2.class, jsonData); @@ -140,8 +155,10 @@ public void read(Json json, JsonValue jsonData) { float worldScale = EngineAssetManager.getInstance().getScale(); bbox.setPosition(pos.x * worldScale, pos.y * worldScale); bbox.setScale(worldScale, worldScale); - - visible = json.readValue("visible", boolean.class, visible, jsonData); } + + visible = json.readValue("visible", boolean.class, visible, jsonData); + + dirtyProps = json.readValue("dirtyProps", long.class, 0L, jsonData); } } diff --git a/blade-engine/src/com/bladecoder/engine/model/CharacterActor.java b/blade-engine/src/com/bladecoder/engine/model/CharacterActor.java index 07d869e27..93864f85c 100644 --- a/blade-engine/src/com/bladecoder/engine/model/CharacterActor.java +++ b/blade-engine/src/com/bladecoder/engine/model/CharacterActor.java @@ -57,6 +57,8 @@ public Color getTextColor() { public void setTextColor(Color textColor) { this.textColor = textColor; + + setDirtyProp(DirtyProps.TEXT_COLOR); } public String getTextStyle() { @@ -65,6 +67,7 @@ public String getTextStyle() { public void setTextStyle(String textStyle) { this.textStyle = textStyle; + setDirtyProp(DirtyProps.TEXT_STYLE); } public String getStandAnim() { @@ -104,6 +107,8 @@ public void addDialog(Dialog d) { public void setWalkingSpeed(float s) { walkingSpeed = s; + + setDirtyProp(DirtyProps.WALKING_SPEED); } public float getWalkingSpeed() { @@ -116,6 +121,7 @@ public Vector2 getTalkingTextPos() { public void setTalkingTextPos(Vector2 talkingTextPos) { this.talkingTextPos = talkingTextPos; + setDirtyProp(DirtyProps.TALKING_TEXT_POS); } public void lookat(Vector2 p) { @@ -125,7 +131,7 @@ public void lookat(Vector2 p) { inAnim(); removeTween(SpritePosTween.class); ((AnimationRenderer) renderer).startAnimation(standAnim, Tween.Type.SPRITE_DEFINED, -1, null, - new Vector2(bbox.getX(), bbox.getY()), p); + new Vector2(getBBox().getX(), getBBox().getY()), p); outAnim(Tween.Type.SPRITE_DEFINED); } @@ -171,15 +177,13 @@ public void startWalkAnim(Vector2 p0, Vector2 pf) { /** * Walking Support * - * @param pf - * Final position to walk - * @param cb - * The action callback + * @param pf Final position to walk + * @param cb The action callback */ public void goTo(Vector2 pf, ActionCallback cb, boolean ignoreWalkZone) { EngineLogger.debug(MessageFormat.format("GOTO {0},{1}", pf.x, pf.y)); - Vector2 p0 = new Vector2(bbox.getX(), bbox.getY()); + Vector2 p0 = new Vector2(getBBox().getX(), getBBox().getY()); // stop previous movement if (tweens.size() > 0) { @@ -270,22 +274,40 @@ public void write(Json json) { if (bjson.getMode() == Mode.MODEL) { if (textStyle != null) json.writeValue("textStyle", textStyle); + + if (textColor != null) + json.writeValue("textColor", textColor); + + if (talkingTextPos != null) { + float worldScale = EngineAssetManager.getInstance().getScale(); + json.writeValue("talkingTextPos", + new Vector2(talkingTextPos.x / worldScale, talkingTextPos.y / worldScale)); + } } else { - json.writeValue("standAnim", standAnim); - json.writeValue("walkAnim", walkAnim); - json.writeValue("talkAnim", talkAnim); - } + if (!DEFAULT_STAND_ANIM.equals(standAnim)) + json.writeValue("standAnim", standAnim); - json.writeValue("walkingSpeed", walkingSpeed); + if (!DEFAULT_WALK_ANIM.equals(walkAnim)) + json.writeValue("walkAnim", walkAnim); - if (textColor != null) - json.writeValue("textColor", textColor); + if (!DEFAULT_TALK_ANIM.equals(talkAnim)) + json.writeValue("talkAnim", talkAnim); - if (talkingTextPos != null) { - float worldScale = EngineAssetManager.getInstance().getScale(); - json.writeValue("talkingTextPos", - new Vector2(talkingTextPos.x / worldScale, talkingTextPos.y / worldScale)); + if (isDirty(DirtyProps.TEXT_STYLE)) + json.writeValue("textStyle", textStyle); + + if (isDirty(DirtyProps.TEXT_COLOR)) + json.writeValue("textColor", textColor); + + if (isDirty(DirtyProps.TALKING_TEXT_POS)) { + float worldScale = EngineAssetManager.getInstance().getScale(); + json.writeValue("talkingTextPos", + new Vector2(talkingTextPos.x / worldScale, talkingTextPos.y / worldScale)); + } } + + if (bjson.getMode() == Mode.MODEL || isDirty(DirtyProps.WALKING_SPEED)) + json.writeValue("walkingSpeed", walkingSpeed); } @SuppressWarnings("unchecked") @@ -302,8 +324,6 @@ public void read(Json json, JsonValue jsonData) { d.setActor(this); } - textStyle = json.readValue("textStyle", String.class, jsonData); - } else { if (dialogs != null) { JsonValue dialogsValue = jsonData.get("dialogs"); @@ -317,14 +337,15 @@ public void read(Json json, JsonValue jsonData) { } } - standAnim = json.readValue("standAnim", String.class, jsonData); - walkAnim = json.readValue("walkAnim", String.class, jsonData); - talkAnim = json.readValue("talkAnim", String.class, jsonData); + standAnim = json.readValue("standAnim", String.class, DEFAULT_STAND_ANIM, jsonData); + walkAnim = json.readValue("walkAnim", String.class, DEFAULT_WALK_ANIM, jsonData); + talkAnim = json.readValue("talkAnim", String.class, DEFAULT_TALK_ANIM, jsonData); } + textStyle = json.readValue("textStyle", String.class, textStyle, jsonData); walkingSpeed = json.readValue("walkingSpeed", float.class, walkingSpeed, jsonData); - textColor = json.readValue("textColor", Color.class, jsonData); - talkingTextPos = json.readValue("talkingTextPos", Vector2.class, jsonData); + textColor = json.readValue("textColor", Color.class, textColor, jsonData); + talkingTextPos = json.readValue("talkingTextPos", Vector2.class, talkingTextPos, jsonData); if (talkingTextPos != null) { float worldScale = EngineAssetManager.getInstance().getScale(); diff --git a/blade-engine/src/com/bladecoder/engine/model/Dialog.java b/blade-engine/src/com/bladecoder/engine/model/Dialog.java index 7aa9b6b51..d180a4a78 100644 --- a/blade-engine/src/com/bladecoder/engine/model/Dialog.java +++ b/blade-engine/src/com/bladecoder/engine/model/Dialog.java @@ -28,7 +28,7 @@ public class Dialog implements Serializable { public final static String DEFAULT_DIALOG_VERB = "dialog"; - private ArrayList options = new ArrayList(); + private ArrayList options = new ArrayList<>(); private int currentOption = -1; @@ -60,7 +60,7 @@ public Dialog selectOption(int i) { */ public List getChoices() { ArrayList options = getVisibleOptions(); - List choices = new ArrayList(options.size()); + List choices = new ArrayList<>(options.size()); for (DialogOption o : options) { choices.add(o.getText()); @@ -106,7 +106,7 @@ public ArrayList getOptions() { } private ArrayList getVisibleOptions() { - ArrayList visible = new ArrayList(); + ArrayList visible = new ArrayList<>(); for (DialogOption o : options) { if (o.isVisible()) @@ -172,6 +172,8 @@ public void read(Json json, JsonValue jsonData) { o.read(json, jsonValue); i++; } + + currentOption = json.readValue("currentOption", int.class, jsonData); } } } diff --git a/blade-engine/src/com/bladecoder/engine/model/DirtyProps.java b/blade-engine/src/com/bladecoder/engine/model/DirtyProps.java new file mode 100644 index 000000000..616e76133 --- /dev/null +++ b/blade-engine/src/com/bladecoder/engine/model/DirtyProps.java @@ -0,0 +1,6 @@ +package com.bladecoder.engine.model; + +public enum DirtyProps { + POS, VISIBLE, DESC, ZINDEX, INTERACTION, STATE, LAYER, ROT, SCALEX, SCALEY, TINT, FAKE_DEPTH, BBOX_FROM_RENDERER, + WALKING_SPEED, TEXT_COLOR, TALKING_TEXT_POS, TEXT_STYLE; +} diff --git a/blade-engine/src/com/bladecoder/engine/model/ImageRenderer.java b/blade-engine/src/com/bladecoder/engine/model/ImageRenderer.java index c6dc0635a..640b5ff3d 100644 --- a/blade-engine/src/com/bladecoder/engine/model/ImageRenderer.java +++ b/blade-engine/src/com/bladecoder/engine/model/ImageRenderer.java @@ -19,6 +19,7 @@ import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.Texture.TextureFilter; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Json; @@ -119,7 +120,7 @@ public void startAnimation(String id, Tween.Type repeatType, int count, ActionCa disposeSource(currentAnimation.source); currentAnimation = fa; - currentSource = (ImageCacheEntry) sourceCache.get(fa.source); + currentSource = sourceCache.get(fa.source); // If the source is not loaded. Load it. if (currentSource == null || currentSource.refCounter < 1) { @@ -128,7 +129,7 @@ public void startAnimation(String id, Tween.Type repeatType, int count, ActionCa retrieveSource(fa.source); - currentSource = (ImageCacheEntry) sourceCache.get(fa.source); + currentSource = sourceCache.get(fa.source); if (currentSource == null) { EngineLogger.error("Could not load AnimationDesc: " + id); @@ -210,11 +211,13 @@ private void retrieveSource(String source) { ((ImageCacheEntry) entry).tex = EngineAssetManager.getInstance() .getTexture(EngineAssetManager.IMAGE_DIR + source); + + ((ImageCacheEntry) entry).tex.setFilter(TextureFilter.Linear, TextureFilter.Linear); } } private String getI18NSource(String source) { - String lang = I18N.getCurrentLocale().getLanguage(); + String lang = world.getI18N().getCurrentLocale().getLanguage(); int pointIdx = source.lastIndexOf('.'); String ext = source.substring(pointIdx); diff --git a/blade-engine/src/com/bladecoder/engine/model/InteractiveActor.java b/blade-engine/src/com/bladecoder/engine/model/InteractiveActor.java index 51d807def..dae7999c7 100644 --- a/blade-engine/src/com/bladecoder/engine/model/InteractiveActor.java +++ b/blade-engine/src/com/bladecoder/engine/model/InteractiveActor.java @@ -56,6 +56,8 @@ public class InteractiveActor extends BaseActor implements Comparable= 0 && currentMusicDelay > desc.getRepeatDelay() + desc.getInitialDelay()) { - currentMusicDelay = desc.getInitialDelay(); + currentMusicDelay = desc.getInitialDelay() + delta; playMusic(); } } @@ -206,7 +211,13 @@ public void dispose() { public void loadAssets() { if (music == null && desc != null) { EngineLogger.debug("LOADING MUSIC: " + desc.getFilename()); - EngineAssetManager.getInstance().loadMusic(desc.getFilename()); + + try { + EngineAssetManager.getInstance().loadMusic(desc.getFilename()); + } catch (FileNotFoundException e) { + EngineLogger.error("Not found: " + e.getLocalizedMessage()); + desc = null; + } } } diff --git a/blade-engine/src/com/bladecoder/engine/model/ObstacleActor.java b/blade-engine/src/com/bladecoder/engine/model/ObstacleActor.java index ea2b8fbe3..3a4f40e03 100644 --- a/blade-engine/src/com/bladecoder/engine/model/ObstacleActor.java +++ b/blade-engine/src/com/bladecoder/engine/model/ObstacleActor.java @@ -27,14 +27,15 @@ */ public class ObstacleActor extends BaseActor { + @Override public void setVisible(boolean visible) { - this.visible = visible; + super.setVisible(visible); if (scene != null && scene.getPolygonalNavGraph() != null) { if (visible) - scene.getPolygonalNavGraph().addDinamicObstacle(bbox); + scene.getPolygonalNavGraph().addDinamicObstacle(getBBox()); else - scene.getPolygonalNavGraph().removeDinamicObstacle(bbox); + scene.getPolygonalNavGraph().removeDinamicObstacle(getBBox()); } } @@ -42,17 +43,18 @@ public void setVisible(boolean visible) { public void update(float delta) { } + @Override public void setPosition(float x, float y) { boolean inNavGraph = false; if (scene != null && scene.getPolygonalNavGraph() != null) { - inNavGraph = scene.getPolygonalNavGraph().removeDinamicObstacle(bbox); + inNavGraph = scene.getPolygonalNavGraph().removeDinamicObstacle(getBBox()); } - bbox.setPosition(x, y); + getBBox().setPosition(x, y); if (inNavGraph) { - scene.getPolygonalNavGraph().addDinamicObstacle(bbox); + scene.getPolygonalNavGraph().addDinamicObstacle(getBBox()); } } @@ -60,8 +62,8 @@ public void setPosition(float x, float y) { public void write(Json json) { BladeJson bjson = (BladeJson) json; if (bjson.getMode() == Mode.MODEL) { - PolygonUtils.ensureClockWise(bbox.getVertices(), 0, bbox.getVertices().length); - bbox.dirty(); + PolygonUtils.ensureClockWise(getBBox().getVertices(), 0, getBBox().getVertices().length); + getBBox().dirty(); } super.write(json); diff --git a/blade-engine/src/com/bladecoder/engine/model/ParticleRenderer.java b/blade-engine/src/com/bladecoder/engine/model/ParticleRenderer.java index 23ca5d999..e38835225 100644 --- a/blade-engine/src/com/bladecoder/engine/model/ParticleRenderer.java +++ b/blade-engine/src/com/bladecoder/engine/model/ParticleRenderer.java @@ -222,4 +222,10 @@ public void read(Json json, JsonValue jsonData) { lastAnimationTime = json.readValue("lastAnimationTime", Float.class, jsonData); } } + + @Override + public void setWorld(World world) { + // TODO Auto-generated method stub + + } } \ No newline at end of file diff --git a/blade-engine/src/com/bladecoder/engine/model/Scene.java b/blade-engine/src/com/bladecoder/engine/model/Scene.java index b189462fa..eed8eca6d 100644 --- a/blade-engine/src/com/bladecoder/engine/model/Scene.java +++ b/blade-engine/src/com/bladecoder/engine/model/Scene.java @@ -18,6 +18,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import com.badlogic.gdx.graphics.Color; @@ -186,10 +188,6 @@ public void init() { timers.clear(); textManager.reset(); - - // Run INIT action - if (getVerb("init") != null) - runVerb("init"); } public VerbManager getVerbManager() { @@ -642,15 +640,15 @@ public void retrieveAssets() { camera.setScrollingDimensions(sceneSize.x * scale, sceneSize.y * scale); } + soundManager.retrieveAssets(); + textManager.getVoiceManager().retrieveAssets(); + // RETRIEVE ACTORS for (BaseActor a : actors.values()) { if (a instanceof AssetConsumer) ((AssetConsumer) a).retrieveAssets(); } - soundManager.retrieveAssets(); - textManager.getVoiceManager().retrieveAssets(); - if (getWorld().getListener() != null) getWorld().getListener().text(textManager.getCurrentText()); } @@ -662,8 +660,6 @@ public void dispose() { EngineAssetManager.getInstance().disposeAtlas(backgroundAtlas); } - // orderedActors.clear(); - for (BaseActor a : actors.values()) { if (a instanceof AssetConsumer) ((AssetConsumer) a).dispose(); @@ -694,12 +690,16 @@ public PolygonalNavGraph getPolygonalNavGraph() { @Override public void write(Json json) { BladeJson bjson = (BladeJson) json; + bjson.setScene(this); + if (bjson.getMode() == Mode.MODEL) { json.writeValue("id", id); json.writeValue("layers", layers, layers.getClass(), SceneLayer.class); - json.writeValue("actors", actors); + SortedMap sortedActors = new TreeMap<>(); + sortedActors.putAll(actors); + json.writeValue("actors", sortedActors); if (backgroundAtlas != null) { json.writeValue("backgroundAtlas", backgroundAtlas); @@ -718,7 +718,9 @@ public void write(Json json) { SceneActorRef actorRef; json.writeObjectStart("actors"); - for (BaseActor a : actors.values()) { + SortedMap sortedActors = new TreeMap<>(); + sortedActors.putAll(actors); + for (BaseActor a : sortedActors.values()) { actorRef = new SceneActorRef(a.getInitScene(), a.getId()); json.writeValue(actorRef.toString(), a); } @@ -747,12 +749,15 @@ public void write(Json json) { json.writeValue("player", player); json.writeValue("walkZone", walkZone); + bjson.setScene(null); } @SuppressWarnings("unchecked") @Override public void read(Json json, JsonValue jsonData) { BladeJson bjson = (BladeJson) json; + bjson.setScene(this); + if (bjson.getMode() == Mode.MODEL) { id = json.readValue("id", String.class, jsonData); @@ -780,6 +785,9 @@ public void read(Json json, JsonValue jsonData) { depthVector = json.readValue("depthVector", Vector2.class, jsonData); + sceneSize = json.readValue("sceneSize", Vector2.class, jsonData); + + // FOR BACKWARDS COMPATIBILITY if (jsonData.get("polygonalNavGraph") != null) { JsonValue jsonValuePNG = jsonData.get("polygonalNavGraph"); @@ -792,9 +800,9 @@ public void read(Json json, JsonValue jsonData) { WalkZoneActor wz = new WalkZoneActor(); wz.setId("walkzone"); - wz.bbox.setVertices(walkZonePol.getVertices()); - wz.bbox.setScale(walkZonePol.getScaleX(), walkZonePol.getScaleY()); - wz.bbox.setPosition(walkZonePol.getX(), walkZonePol.getY()); + wz.getBBox().setVertices(walkZonePol.getVertices()); + wz.getBBox().setScale(walkZonePol.getScaleX(), walkZonePol.getScaleY()); + wz.getBBox().setPosition(walkZonePol.getX(), walkZonePol.getY()); wz.setScene(this); wz.setInitScene(id); @@ -802,8 +810,6 @@ public void read(Json json, JsonValue jsonData) { walkZone = wz.getId(); } - sceneSize = json.readValue("sceneSize", Vector2.class, jsonData); - } else { JsonValue jsonValueActors = jsonData.get("actors"); SceneActorRef actorRef; @@ -862,5 +868,7 @@ public void read(Json json, JsonValue jsonData) { if (jsonData.get("walkZone") != null) walkZone = json.readValue("walkZone", String.class, jsonData); + + bjson.setScene(null); } } diff --git a/blade-engine/src/com/bladecoder/engine/model/SceneCamera.java b/blade-engine/src/com/bladecoder/engine/model/SceneCamera.java index 88a23d017..5b00d60ae 100644 --- a/blade-engine/src/com/bladecoder/engine/model/SceneCamera.java +++ b/blade-engine/src/com/bladecoder/engine/model/SceneCamera.java @@ -31,11 +31,11 @@ import com.bladecoder.engine.assets.EngineAssetManager; import com.bladecoder.engine.util.InterpolationMode; -public class SceneCamera extends OrthographicCamera implements Serializable { - +public class SceneCamera extends OrthographicCamera implements Serializable { + private static final float START_SCROLLX = 0.1f; private static final float START_SCROLLY = 0.15f; - + // to avoid create new vector when calling getPosition private final static Vector2 tmpPos = new Vector2(); @@ -43,96 +43,93 @@ public class SceneCamera extends OrthographicCamera implements Serializable { private float startScrollDistanceY; private float scrollingWidth, scrollingHeight; - + private CameraTween cameraTween; - + private Matrix4 parallaxView = new Matrix4(); private Matrix4 parallaxCombined = new Matrix4(); private Vector3 tmp = new Vector3(); private Vector3 tmp2 = new Vector3(); - + public SceneCamera() { } - + public void create(float worldWidth, float worldHeight) { scrollingWidth = worldWidth; scrollingHeight = worldHeight; - + zoom = 1.0f; - + setToOrtho(false, worldWidth, worldHeight); update(); - - startScrollDistanceX = worldWidth * START_SCROLLX; + + startScrollDistanceX = worldWidth * START_SCROLLX; startScrollDistanceY = worldHeight * START_SCROLLY; } - - public float getWidth() { - return viewportWidth; - } - - public float getHeight() { - return viewportHeight; - } - + public float getScrollingWidth() { return scrollingWidth; } - + public float getScrollingHeight() { return scrollingHeight; } public void setScrollingDimensions(float w, float h) { scrollingWidth = Math.max(w, viewportWidth); - scrollingHeight = Math.max(h, viewportHeight); + scrollingHeight = Math.max(h, viewportHeight); } - + public void update(float delta) { - if(cameraTween != null) { + if (cameraTween != null) { cameraTween.update(delta); - if(cameraTween.isComplete()) { + if (cameraTween != null && cameraTween.isComplete()) { cameraTween = null; } } - } + } public void setPosition(float x, float y) { float maxleft = viewportWidth / 2 * zoom; float maxright = (scrollingWidth - viewportWidth / 2 * zoom); - + float maxbottom = viewportHeight / 2 * zoom; float maxtop = (scrollingHeight - viewportHeight / 2 * zoom); - x = MathUtils.clamp(x, maxleft, maxright); + x = MathUtils.clamp(x, maxleft, maxright); y = MathUtils.clamp(y, maxbottom, maxtop); position.set(x, y, 0); - + update(); } - + public void setZoom(float zoom) { this.zoom = zoom; update(); } - + public Vector2 getPosition() { Vector3 p = position; return tmpPos.set(p.x, p.y); } - + public float getZoom() { return zoom; } + public void stopAnim() { + cameraTween = null; + } + /** * Create camera animation. */ - public void startAnimation(float destX, float destY, float zoom, float duration, InterpolationMode interpolation, ActionCallback cb) { + public void startAnimation(float destX, float destY, float zoom, float duration, InterpolationMode interpolation, + ActionCallback cb) { cameraTween = new CameraTween(); - + cameraTween.start(this, Tween.Type.NO_REPEAT, 1, destX, destY, zoom, duration, interpolation, cb); } @@ -140,8 +137,8 @@ public void getInputUnProject(Viewport viewport, Vector3 out) { out.set(Gdx.input.getX(), Gdx.input.getY(), 0); - unproject(out, viewport.getScreenX(), viewport.getScreenY(), - viewport.getScreenWidth(), viewport.getScreenHeight()); + unproject(out, viewport.getScreenX(), viewport.getScreenY(), viewport.getScreenWidth(), + viewport.getScreenHeight()); out.x = MathUtils.clamp(out.x, 0, scrollingWidth - 1); out.y = MathUtils.clamp(out.y, 0, scrollingHeight - 1); @@ -152,7 +149,7 @@ public void updatePos(SpriteActor followActor) { float posx = followActor.getX(); float cy = position.y; float posy = followActor.getY(); - + boolean translate = false; if (cx - posx > startScrollDistanceX * zoom) { @@ -162,16 +159,16 @@ public void updatePos(SpriteActor followActor) { cx = cx + (posx - cx - startScrollDistanceX * zoom); translate = true; } - - if (cy - posy + followActor.getHeight() > startScrollDistanceY * zoom) { + + if (cy - posy + followActor.getHeight() > startScrollDistanceY * zoom) { cy = cy - (cy - posy - startScrollDistanceY * zoom); translate = true; } else if (posy - cy > startScrollDistanceY * zoom) { cy = cy + (posy - cy - startScrollDistanceY * zoom); translate = true; } - - if(translate) { + + if (translate) { setPosition(cx, cy); } } @@ -179,61 +176,61 @@ public void updatePos(SpriteActor followActor) { public void scene2screen(Viewport viewport, Vector3 out) { project(out, 0, 0, viewport.getScreenWidth(), viewport.getScreenHeight()); } - - public Matrix4 calculateParallaxMatrix (float parallaxX, float parallaxY) { + + public Matrix4 calculateParallaxMatrix(float parallaxX, float parallaxY) { update(); tmp.set(position); // tmp.x *= parallaxX; tmp.y *= parallaxY; - - tmp.x = (tmp.x - scrollingWidth / 2) * parallaxX + scrollingWidth / 2; + + tmp.x = (tmp.x - scrollingWidth / 2) * parallaxX + scrollingWidth / 2; parallaxView.setToLookAt(tmp, tmp2.set(tmp).add(direction), up); parallaxCombined.set(projection); Matrix4.mul(parallaxCombined.val, parallaxView.val); return parallaxCombined; } - + @Override public void write(Json json) { float worldScale = EngineAssetManager.getInstance().getScale(); - + json.writeValue("width", viewportWidth / worldScale); json.writeValue("height", viewportHeight / worldScale); json.writeValue("scrollingWidth", scrollingWidth / worldScale); json.writeValue("scrollingHeight", scrollingHeight / worldScale); - + Vector2 p = getPosition(); - p.x = p.x/worldScale; - p.y = p.y/worldScale; + p.x = p.x / worldScale; + p.y = p.y / worldScale; json.writeValue("pos", p); json.writeValue("zoom", getZoom()); - - if(cameraTween != null) + + if (cameraTween != null) json.writeValue("cameraTween", cameraTween); } @Override public void read(Json json, JsonValue jsonData) { float worldScale = EngineAssetManager.getInstance().getScale(); - + viewportWidth = json.readValue("width", Float.class, jsonData) * worldScale; viewportHeight = json.readValue("height", Float.class, jsonData) * worldScale; scrollingWidth = json.readValue("scrollingWidth", Float.class, jsonData) * worldScale; scrollingHeight = json.readValue("scrollingHeight", Float.class, jsonData) * worldScale; Vector2 pos = json.readValue("pos", Vector2.class, jsonData); - pos.x *= worldScale; - pos.y *= worldScale; + pos.x *= worldScale; + pos.y *= worldScale; float z = json.readValue("zoom", Float.class, jsonData); - + create(viewportWidth, viewportHeight); this.zoom = z; position.set(pos.x, pos.y, 0); update(); cameraTween = json.readValue("cameraTween", CameraTween.class, jsonData); - if(cameraTween != null) { + if (cameraTween != null) { cameraTween.setTarget(this); } - } + } } diff --git a/blade-engine/src/com/bladecoder/engine/model/SceneSoundManager.java b/blade-engine/src/com/bladecoder/engine/model/SceneSoundManager.java index 6ebf3862b..73dca9f40 100644 --- a/blade-engine/src/com/bladecoder/engine/model/SceneSoundManager.java +++ b/blade-engine/src/com/bladecoder/engine/model/SceneSoundManager.java @@ -1,5 +1,6 @@ package com.bladecoder.engine.model; +import java.io.FileNotFoundException; import java.util.HashMap; import java.util.Map.Entry; @@ -18,12 +19,12 @@ */ public class SceneSoundManager implements Serializable, AssetConsumer { - private HashMap loadedSounds = new HashMap(0); + private HashMap loadedSounds = new HashMap<>(0); private final World w; - + // the global configurable by user volume public static float VOLUME_MULTIPLIER = 1f; - + public SceneSoundManager(World w) { this.w = w; } @@ -45,7 +46,14 @@ public void playSound(String id) { s = loadedSounds.get(id); EngineLogger.debug("LOADING SOUND: " + s.desc.getId() + " - " + s.desc.getFilename()); - EngineAssetManager.getInstance().loadSound(s.desc.getFilename()); + + try { + EngineAssetManager.getInstance().loadSound(s.desc.getFilename()); + } catch (FileNotFoundException e) { + EngineLogger.error("Sound file not found:" + e.getMessage()); + return; + } + EngineAssetManager.getInstance().finishLoading(); s.sound = EngineAssetManager.getInstance().getSound(s.desc.getFilename()); } @@ -114,8 +122,7 @@ public void pause() { @Override public void dispose() { for (LoadedSound s : loadedSounds.values()) { - // EngineLogger.debug("DISPOSING SOUND: " + s.desc.getId() + " - " + - // s.desc.getFilename()); + if (s.playing) s.sound.stop(); @@ -126,9 +133,14 @@ public void dispose() { @Override public void loadAssets() { for (LoadedSound s : loadedSounds.values()) { - // EngineLogger.debug("LOADING SOUND: " + s.desc.getId() + " - " + - // s.desc.getFilename()); - EngineAssetManager.getInstance().loadSound(s.desc.getFilename()); + + try { + EngineAssetManager.getInstance().loadSound(s.desc.getFilename()); + } catch (FileNotFoundException e) { + loadedSounds.remove(s.desc.getId()); + EngineLogger.error("Sound file not found:" + e.getMessage()); + } + } } @@ -150,16 +162,16 @@ public void retrieveAssets() { @Override public void write(Json json) { - json.writeValue("loadedSounds", loadedSounds, loadedSounds.getClass(), SoundDesc.class); + json.writeValue("loadedSounds", loadedSounds, loadedSounds.getClass(), LoadedSound.class); } @SuppressWarnings("unchecked") @Override public void read(Json json, JsonValue jsonData) { - loadedSounds = json.readValue("loadedSounds", HashMap.class, SoundDesc.class, jsonData); + loadedSounds = json.readValue("loadedSounds", HashMap.class, LoadedSound.class, jsonData); if (loadedSounds == null) - loadedSounds = new HashMap(0); + loadedSounds = new HashMap<>(0); // Retrieve desc from World sound description. for (Entry e : loadedSounds.entrySet()) { @@ -167,7 +179,7 @@ public void read(Json json, JsonValue jsonData) { } } - static class LoadedSound { + public static class LoadedSound { transient SoundDesc desc; transient Sound sound; diff --git a/blade-engine/src/com/bladecoder/engine/model/Sprite3DRenderer.java b/blade-engine/src/com/bladecoder/engine/model/Sprite3DRenderer.java index adba4bcc8..956e9aff4 100644 --- a/blade-engine/src/com/bladecoder/engine/model/Sprite3DRenderer.java +++ b/blade-engine/src/com/bladecoder/engine/model/Sprite3DRenderer.java @@ -738,7 +738,8 @@ public void write(Json json) { } else { if (animationCb != null) - json.writeValue("animationCb", ActionCallbackSerializer.find(bjson.getWorld(), animationCb)); + json.writeValue("animationCb", + ActionCallbackSerializer.find(bjson.getWorld(), bjson.getScene(), animationCb)); json.writeValue("currentCount", currentCount); json.writeValue("currentAnimationType", currentAnimationType); @@ -770,7 +771,7 @@ public void read(Json json, JsonValue jsonData) { renderShadow = json.readValue("renderShadow", Boolean.class, jsonData); } else { - animationCb = ActionCallbackSerializer.find(bjson.getWorld(), + animationCb = ActionCallbackSerializer.find(bjson.getWorld(), bjson.getScene(), json.readValue("animationCb", String.class, jsonData)); currentCount = json.readValue("currentCount", Integer.class, jsonData); diff --git a/blade-engine/src/com/bladecoder/engine/model/SpriteActor.java b/blade-engine/src/com/bladecoder/engine/model/SpriteActor.java index 8ff8f20c4..121ad6ca4 100644 --- a/blade-engine/src/com/bladecoder/engine/model/SpriteActor.java +++ b/blade-engine/src/com/bladecoder/engine/model/SpriteActor.java @@ -64,6 +64,7 @@ public boolean getFakeDepth() { public void setFakeDepth(boolean fd) { fakeDepth = fd; + setDirtyProp(DirtyProps.FAKE_DEPTH); } @Override @@ -89,9 +90,11 @@ public void setBboxFromRenderer(boolean v) { this.bboxFromRenderer = v; if (v) - renderer.updateBboxFromRenderer(bbox); + renderer.updateBboxFromRenderer(getBBox()); else renderer.updateBboxFromRenderer(null); + + setDirtyProp(DirtyProps.BBOX_FROM_RENDERER); } public float getWidth() { @@ -105,11 +108,11 @@ public float getHeight() { public float getScale() { return scaleX; } - + public float getScaleX() { return scaleX; } - + public float getScaleY() { return scaleY; } @@ -120,27 +123,32 @@ public Color getTint() { public void setTint(Color tint) { this.tint = tint; + setDirtyProp(DirtyProps.TINT); } public void setScale(float scale) { setScale(scale, scale); } - + public void setScale(float scaleX, float scaleY) { this.scaleX = scaleX; this.scaleY = scaleY; if (bboxFromRenderer) - bbox.setScale(scaleX, scaleY); + getBBox().setScale(scaleX, scaleY); else { float worldScale = EngineAssetManager.getInstance().getScale(); - bbox.setScale(scaleX * worldScale, scaleY * worldScale); + getBBox().setScale(scaleX * worldScale, scaleY * worldScale); } + + setDirtyProp(DirtyProps.SCALEX); + setDirtyProp(DirtyProps.SCALEY); } public void setRot(float rot) { this.rot = rot; - bbox.setRotation(rot); + getBBox().setRotation(rot); + setDirtyProp(DirtyProps.ROT); } public float getRot() { @@ -151,7 +159,7 @@ public float getRot() { public void update(float delta) { super.update(delta); - if (visible) { + if (isVisible()) { renderer.update(delta); for (int i = 0; i < tweens.size(); i++) { @@ -271,7 +279,7 @@ protected void outAnim(Type repeatType) { public void addTween(Tween tween) { removeTween(tween.getClass()); - + tweens.add(tween); } @@ -298,7 +306,7 @@ public void retrieveAssets() { renderer.retrieveAssets(); // Call setPosition to recalc fake depth and camera follow - setPosition(bbox.getX(), bbox.getY()); + setPosition(getBBox().getX(), getBBox().getY()); } @Override @@ -314,33 +322,46 @@ public void write(Json json) { // Reset vertices if bboxFromRenderer to save always with 0.0 value if (bboxFromRenderer && bjson.getMode() == Mode.MODEL) { - float[] verts = bbox.getVertices(); - bbox.setVertices(new float[8]); + float[] verts = getBBox().getVertices(); + getBBox().setVertices(new float[8]); super.write(json); - bbox.setVertices(verts); + getBBox().setVertices(verts); } else { super.write(json); } - + if (bjson.getMode() == Mode.MODEL) { json.writeValue("renderer", renderer, null); + + if (tint != null) + json.writeValue("tint", tint); } else { json.writeValue("renderer", renderer); json.writeValue("tweens", tweens, ArrayList.class, Tween.class); - json.writeValue("playingSound", playingSound); + + if (playingSound != null) + json.writeValue("playingSound", playingSound); + + if (isDirty(DirtyProps.TINT)) + json.writeValue("tint", tint); } - json.writeValue("scaleX", scaleX); - json.writeValue("scaleY", scaleY); - json.writeValue("rot", rot); - - if(tint != null) - json.writeValue("tint", tint); - - json.writeValue("fakeDepth", fakeDepth); - json.writeValue("bboxFromRenderer", bboxFromRenderer); + if (bjson.getMode() == Mode.MODEL || isDirty(DirtyProps.SCALEX)) + json.writeValue("scaleX", scaleX); + + if (bjson.getMode() == Mode.MODEL || isDirty(DirtyProps.SCALEY)) + json.writeValue("scaleY", scaleY); + + if (bjson.getMode() == Mode.MODEL || isDirty(DirtyProps.ROT)) + json.writeValue("rot", rot); + + if (bjson.getMode() == Mode.MODEL || isDirty(DirtyProps.FAKE_DEPTH)) + json.writeValue("fakeDepth", fakeDepth); + + if (bjson.getMode() == Mode.MODEL || isDirty(DirtyProps.BBOX_FROM_RENDERER)) + json.writeValue("bboxFromRenderer", bboxFromRenderer); } @SuppressWarnings("unchecked") @@ -354,11 +375,11 @@ public void read(Json json, JsonValue jsonData) { } else { tweens = json.readValue("tweens", ArrayList.class, Tween.class, jsonData); - if(tweens == null) { + if (tweens == null) { EngineLogger.debug("Couldn't load state of actor: " + id); return; } - + for (Tween t : tweens) t.setTarget(this); @@ -367,6 +388,8 @@ public void read(Json json, JsonValue jsonData) { playingSound = json.readValue("playingSound", String.class, jsonData); } + renderer.setWorld(bjson.getWorld()); + if (jsonData.get("scale") != null) { scaleX = json.readValue("scale", float.class, jsonData); scaleY = scaleX; @@ -374,7 +397,7 @@ public void read(Json json, JsonValue jsonData) { scaleX = json.readValue("scaleX", float.class, scaleX, jsonData); scaleY = json.readValue("scaleY", float.class, scaleY, jsonData); } - + rot = json.readValue("rot", float.class, rot, jsonData); tint = json.readValue("tint", Color.class, tint, jsonData); @@ -390,10 +413,13 @@ public void read(Json json, JsonValue jsonData) { bboxFromRenderer = json.readValue("bboxFromRenderer", boolean.class, bboxFromRenderer, jsonData); if (bboxFromRenderer) - renderer.updateBboxFromRenderer(bbox); + renderer.updateBboxFromRenderer(getBBox()); setScale(scaleX, scaleY); setRot(rot); + + // restore dirtyProps after rotation and scale + dirtyProps = json.readValue("dirtyProps", long.class, 0L, jsonData); } } \ No newline at end of file diff --git a/blade-engine/src/com/bladecoder/engine/model/Text.java b/blade-engine/src/com/bladecoder/engine/model/Text.java index 9b610a76d..952e03c0f 100644 --- a/blade-engine/src/com/bladecoder/engine/model/Text.java +++ b/blade-engine/src/com/bladecoder/engine/model/Text.java @@ -97,8 +97,11 @@ public void write(Json json) { json.writeValue("voiceId", voiceId); json.writeValue("animation", animation); - if (cb != null) - json.writeValue("cb", ActionCallbackSerializer.find(((BladeJson) json).getWorld(), cb)); + if (cb != null) { + World w = ((BladeJson) json).getWorld(); + Scene s = ((BladeJson) json).getScene(); + json.writeValue("cb", ActionCallbackSerializer.find(w, s, cb)); + } } @Override @@ -113,6 +116,8 @@ public void read(Json json, JsonValue jsonData) { actorId = json.readValue("actorId", String.class, jsonData); voiceId = json.readValue("voiceId", String.class, jsonData); animation = json.readValue("animation", String.class, jsonData); - cb = ActionCallbackSerializer.find(((BladeJson) json).getWorld(), json.readValue("cb", String.class, jsonData)); + BladeJson bjson = (BladeJson) json; + cb = ActionCallbackSerializer.find(bjson.getWorld(), bjson.getScene(), + json.readValue("cb", String.class, jsonData)); } } diff --git a/blade-engine/src/com/bladecoder/engine/model/TextManager.java b/blade-engine/src/com/bladecoder/engine/model/TextManager.java index 7510f0a94..023ef898f 100644 --- a/blade-engine/src/com/bladecoder/engine/model/TextManager.java +++ b/blade-engine/src/com/bladecoder/engine/model/TextManager.java @@ -46,7 +46,7 @@ public class TextManager implements Serializable { public static final float RECT_MARGIN = 18f; public static final float RECT_BORDER = 2f; - public static final boolean AUTO_HIDE_TEXTS = Config.getProperty(Config.AUTO_HIDE_TEXTS, true); + public static final boolean AUTO_HIDE_TEXTS = Config.getInstance().getProperty(Config.AUTO_HIDE_TEXTS, true); private float inScreenTime; private Text currentText = null; @@ -69,7 +69,7 @@ public void addText(String str, float x, float y, boolean queue, Text.Type type, String actorId, String voiceId, String talkAnimation, ActionCallback cb) { if (str.charAt(0) == I18N.PREFIX) - str = I18N.getString(str.substring(1)); + str = scene.getWorld().getI18N().getString(str.substring(1)); String s = str.replace("\\n", "\n"); @@ -148,7 +148,8 @@ public Text getCurrentText() { } private void setCurrentText(Text t) { - if (currentText != null && currentText.type == Type.TALK && currentText.actorId != null) { + if (currentText != null && (currentText.type == Type.TALK || currentText.animation != null) + && currentText.actorId != null) { CharacterActor a = (CharacterActor) scene.getActor(currentText.actorId, false); // restore previous stand animation @@ -165,7 +166,7 @@ private void setCurrentText(Text t) { voiceManager.play(t.voiceId); // Start talk animation - if (t.type == Type.TALK && t.actorId != null) { + if ((t.type == Type.TALK || t.animation != null) && t.actorId != null) { CharacterActor a = (CharacterActor) scene.getActor(t.actorId, false); previousCharacterAnim = ((AnimationRenderer) a.getRenderer()).getCurrentAnimationId(); diff --git a/blade-engine/src/com/bladecoder/engine/model/TextRenderer.java b/blade-engine/src/com/bladecoder/engine/model/TextRenderer.java index 438e63557..0bf58f50e 100644 --- a/blade-engine/src/com/bladecoder/engine/model/TextRenderer.java +++ b/blade-engine/src/com/bladecoder/engine/model/TextRenderer.java @@ -59,6 +59,8 @@ public class TextRenderer implements ActorRenderer { // with the translation is not ready. private transient String editorTranslatedText; + private World world; + public TextRenderer() { } @@ -93,7 +95,7 @@ public void draw(SpriteBatch batch, float x, float y, float scaleX, float scaleY String tt = text; if (tt.charAt(0) == I18N.PREFIX) - tt = I18N.getString(tt.substring(1)); + tt = world.getI18N().getString(tt.substring(1)); if (editorTranslatedText != null) tt = editorTranslatedText; @@ -180,13 +182,14 @@ public String getText() { } public void setText(String text) { - this.text = text; - this.editorTranslatedText = text; + setText(text, text); } public void setText(String text, String translatedText) { this.text = text; this.editorTranslatedText = translatedText; + + updateLayout(); } public String getFontName() { @@ -302,7 +305,25 @@ public void loadAssets() { params.fontParameters.magFilter = TextureFilter.Linear; params.fontParameters.minFilter = TextureFilter.Linear; - EngineAssetManager.getInstance().load(fontName + getFontSize() + ".ttf", BitmapFont.class, params); + EngineAssetManager.getInstance().load(fontName + getFontSize() + EngineAssetManager.FONT_EXT, BitmapFont.class, + params); + } + + private void updateLayout() { + if (font == null) + return; + + String tt = editorTranslatedText; + + if (tt == null) + tt = text; + + if (tt.charAt(0) == I18N.PREFIX) + tt = world.getI18N().getString(tt.substring(1)); + + layout.setText(font, tt, color, 0, textAlign, false); + + computeBbox(); } @Override @@ -315,17 +336,7 @@ public void retrieveAssets() { font = EngineAssetManager.getInstance().get(fontName + getFontSize() + ".ttf", BitmapFont.class); - String tt = text; - - if (tt.charAt(0) == I18N.PREFIX) - tt = I18N.getString(tt.substring(1)); - - if (editorTranslatedText != null) - tt = editorTranslatedText; - - layout.setText(font, tt, color, 0, textAlign, false); - - computeBbox(); + updateLayout(); } @Override @@ -376,4 +387,9 @@ public void read(Json json, JsonValue jsonData) { } } + + @Override + public void setWorld(World world) { + this.world = world; + } } \ No newline at end of file diff --git a/blade-engine/src/com/bladecoder/engine/model/Transition.java b/blade-engine/src/com/bladecoder/engine/model/Transition.java index 011a03b61..08037bc17 100644 --- a/blade-engine/src/com/bladecoder/engine/model/Transition.java +++ b/blade-engine/src/com/bladecoder/engine/model/Transition.java @@ -50,7 +50,7 @@ public void update(float delta) { // must stay in screen even when finished if (type == Type.FADE_IN) reset(); - + if (cb != null) { ActionCallback tmpcb = cb; cb = null; @@ -102,9 +102,12 @@ public void write(Json json) { json.writeValue("time", time); json.writeValue("color", c); json.writeValue("type", type); - - if(cb != null) - json.writeValue("cb", ActionCallbackSerializer.find(((BladeJson) json).getWorld(), cb)); + + if (cb != null) { + World w = ((BladeJson) json).getWorld(); + Scene s = ((BladeJson) json).getScene(); + json.writeValue("cb", ActionCallbackSerializer.find(w, s, cb)); + } } @Override @@ -113,6 +116,8 @@ public void read(Json json, JsonValue jsonData) { time = json.readValue("time", Float.class, jsonData); c = json.readValue("color", Color.class, jsonData); type = json.readValue("type", Type.class, jsonData); - cb = ActionCallbackSerializer.find(((BladeJson) json).getWorld(), json.readValue("cb", String.class, jsonData)); + BladeJson bjson = (BladeJson) json; + cb = ActionCallbackSerializer.find(bjson.getWorld(), bjson.getScene(), + json.readValue("cb", String.class, jsonData)); } } diff --git a/blade-engine/src/com/bladecoder/engine/model/UIActors.java b/blade-engine/src/com/bladecoder/engine/model/UIActors.java index 2351503ce..6bc9e6234 100644 --- a/blade-engine/src/com/bladecoder/engine/model/UIActors.java +++ b/blade-engine/src/com/bladecoder/engine/model/UIActors.java @@ -1,6 +1,7 @@ package com.bladecoder.engine.model; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import com.badlogic.gdx.graphics.Camera; @@ -60,7 +61,11 @@ public List getActors() { } public void update(float delta) { - for (InteractiveActor a : actors) { + // we use iterator because the actor can be deleted in the update. + Iterator i = actors.iterator(); + + while (i.hasNext()) { + InteractiveActor a = i.next(); a.update(delta); } } @@ -94,9 +99,14 @@ public InteractiveActor getActorAtInput(Viewport v) { cam.getInputUnProject(v, unprojectTmp); + return getActorAt(unprojectTmp.x, unprojectTmp.y); + } + + public InteractiveActor getActorAt(float x, float y) { for (InteractiveActor uia : actors) { - if (uia.canInteract() && uia.hit(unprojectTmp.x, unprojectTmp.y)) + if (uia.canInteract() && uia.hit(x, y)) { return uia; + } } return null; @@ -168,7 +178,6 @@ public void read(Json json, JsonValue jsonData) { // to restore verb cb properly. for (int i = 0; i < jsonValueActors.size; i++) { JsonValue jsonValueAct = jsonValueActors.get(i); - actorRef = new SceneActorRef(jsonValueAct.name); InteractiveActor actor = actors.get(i); actor.read(json, jsonValueAct); diff --git a/blade-engine/src/com/bladecoder/engine/model/Verb.java b/blade-engine/src/com/bladecoder/engine/model/Verb.java index 9bdde2918..c7ee2d246 100644 --- a/blade-engine/src/com/bladecoder/engine/model/Verb.java +++ b/blade-engine/src/com/bladecoder/engine/model/Verb.java @@ -47,7 +47,7 @@ public class Verb implements VerbRunner, Serializable { private String target; private String icon; - private final ArrayList actions = new ArrayList(); + private final ArrayList actions = new ArrayList<>(); private int ip = -1; private String currentTarget; @@ -109,14 +109,17 @@ public void add(Action a) { actions.add(a); } + @Override public ArrayList getActions() { return actions; } + @Override public String getCurrentTarget() { return currentTarget; } + @Override public void run(String currentTarget, ActionCallback cb) { this.currentTarget = currentTarget; this.cb = cb; @@ -151,8 +154,8 @@ public void nextStep() { else ip++; } catch (Exception e) { - EngineLogger.error( - "EXCEPTION EXECUTING ACTION: " + a.getClass().getSimpleName() + " - " + e.getMessage(), e); + EngineLogger.error("EXCEPTION EXECUTING ACTION: " + id + " - " + ip + " - " + + a.getClass().getSimpleName() + " - " + e.getMessage(), e); ip++; } } @@ -179,14 +182,17 @@ public void resume() { nextStep(); } + @Override public int getIP() { return ip; } + @Override public void setIP(int ip) { this.ip = ip; } + @Override public void cancel() { ip = actions.size() + 1; @@ -228,9 +234,9 @@ public void write(Json json) { json.writeArrayEnd(); } else { json.writeValue("ip", ip); - - if(cb != null) - json.writeValue("cb", ActionCallbackSerializer.find(bjson.getWorld(), cb)); + + if (cb != null) + json.writeValue("cb", ActionCallbackSerializer.find(bjson.getWorld(), bjson.getScene(), cb)); if (currentTarget != null) json.writeValue("currentTarget", currentTarget); @@ -275,7 +281,8 @@ public void read(Json json, JsonValue jsonData) { // MUTABLE currentTarget = json.readValue("currentTarget", String.class, (String) null, jsonData); ip = json.readValue("ip", Integer.class, jsonData); - cb = ActionCallbackSerializer.find(bjson.getWorld(), json.readValue("cb", String.class, jsonData)); + cb = ActionCallbackSerializer.find(bjson.getWorld(), bjson.getScene(), + json.readValue("cb", String.class, jsonData)); JsonValue actionsValue = jsonData.get("actions"); diff --git a/blade-engine/src/com/bladecoder/engine/model/VoiceManager.java b/blade-engine/src/com/bladecoder/engine/model/VoiceManager.java index d134ecc81..3f9b50aec 100644 --- a/blade-engine/src/com/bladecoder/engine/model/VoiceManager.java +++ b/blade-engine/src/com/bladecoder/engine/model/VoiceManager.java @@ -30,7 +30,7 @@ public class VoiceManager implements Serializable, AssetConsumer { // the music volume private float volume = 1.0f; - + // the global configurable by user volume public static float VOLUME_MULTIPLIER = 1f; @@ -93,7 +93,7 @@ public void play(String fileName) { // Load and play the voice file in background to avoid // blocking the UI loadAssets(); - + backgroundLoadingTask.cancel(); Timer.schedule(backgroundLoadingTask, 0, 0); } @@ -106,6 +106,10 @@ public void setVolume(float volume) { voice.setVolume(volume * VOLUME_MULTIPLIER); } + public float getVolume() { + return volume; + } + @Override public void dispose() { if (voice != null) { diff --git a/blade-engine/src/com/bladecoder/engine/model/WalkZoneActor.java b/blade-engine/src/com/bladecoder/engine/model/WalkZoneActor.java index a5343fb74..4e69f45a3 100644 --- a/blade-engine/src/com/bladecoder/engine/model/WalkZoneActor.java +++ b/blade-engine/src/com/bladecoder/engine/model/WalkZoneActor.java @@ -31,8 +31,9 @@ public class WalkZoneActor extends BaseActor { public void update(float delta) { } + @Override public void setPosition(float x, float y) { - bbox.setPosition(x, y); + getBBox().setPosition(x, y); if (scene != null && id.equals(scene.getWalkZone())) { scene.getPolygonalNavGraph().createInitialGraph(this, scene.getActors().values()); @@ -43,8 +44,8 @@ public void setPosition(float x, float y) { public void write(Json json) { BladeJson bjson = (BladeJson) json; if (bjson.getMode() == Mode.MODEL) { - PolygonUtils.ensureClockWise(bbox.getVertices(), 0, bbox.getVertices().length); - bbox.dirty(); + PolygonUtils.ensureClockWise(getBBox().getVertices(), 0, getBBox().getVertices().length); + getBBox().dirty(); } super.write(json); diff --git a/blade-engine/src/com/bladecoder/engine/model/World.java b/blade-engine/src/com/bladecoder/engine/model/World.java index 455052c1b..b87d3dd8a 100644 --- a/blade-engine/src/com/bladecoder/engine/model/World.java +++ b/blade-engine/src/com/bladecoder/engine/model/World.java @@ -42,6 +42,7 @@ import com.badlogic.gdx.utils.viewport.Viewport; import com.bladecoder.engine.assets.AssetConsumer; import com.bladecoder.engine.assets.EngineAssetManager; +import com.bladecoder.engine.i18n.I18N; import com.bladecoder.engine.ink.InkManager; import com.bladecoder.engine.serialization.WorldSerialization; import com.bladecoder.engine.util.EngineLogger; @@ -70,6 +71,7 @@ public static enum WorldProperties { private final HashMap sounds = new HashMap<>(); private final HashMap scenes = new HashMap<>(); private final VerbManager verbs = new VerbManager(); + private final I18N i18n = new I18N(); private Scene currentScene; private Dialog currentDialog; @@ -120,6 +122,10 @@ public static enum WorldProperties { // If true call 'initNewGame' or 'initSavedGame' verbs. private boolean initGame; + // The verb to call after loading a scene. If null, the "init" verb will be + // called + private String initVerb; + private final WorldSerialization serialization = new WorldSerialization(this); public World() { @@ -266,16 +272,29 @@ else if (customProperties.get(WorldProperties.SAVED_GAME_VERSION.toString()) != // call 'init' verb only when arrives from setCurrentScene and not // from load or restoring if (initScene) { + currentScene.init(); + // If in test mode run 'test' verb (only the first time) if (testScene != null && testScene.equals(currentScene.getId()) && currentScene.getVerb(Verb.TEST_VERB) != null) { - currentScene.runVerb(Verb.TEST_VERB); + + initVerb = Verb.TEST_VERB; + testScene = null; } - currentScene.init(); + if (initVerb == null) + initVerb = "init"; } + // Run INIT verb + if (initVerb != null && (currentScene.getVerb(initVerb) != null + || getVerbManager().getVerb(initVerb, null, null) != null)) { + currentScene.runVerb(initVerb); + } + + initVerb = null; + } if (paused || assetState != AssetState.LOADED) @@ -348,6 +367,10 @@ public AssetState getAssetState() { return assetState; } + public I18N getI18N() { + return i18n; + } + public Dialog getCurrentDialog() { return currentDialog; } @@ -368,7 +391,7 @@ public void setInitScene(String initScene) { this.initScene = initScene; } - public void setCurrentScene(Scene scene, boolean init) { + public void setCurrentScene(Scene scene, boolean init, String initVerb) { initLoadingTime = System.currentTimeMillis(); @@ -406,6 +429,7 @@ public void setCurrentScene(Scene scene, boolean init) { } currentScene = scene; + this.initVerb = initVerb; musicManager.leaveScene(currentScene.getMusicDesc()); } @@ -445,14 +469,14 @@ public void setCutMode(boolean v) { listener.cutMode(cutMode); } - public void setCurrentScene(String id, boolean init) { + public void setCurrentScene(String id, boolean init, String initVerb) { if (id.equals("$" + WorldProperties.PREVIOUS_SCENE.toString())) id = getCustomProperty(WorldProperties.PREVIOUS_SCENE.toString()); Scene s = scenes.get(id); if (s != null) { - setCurrentScene(s, init); + setCurrentScene(s, init, initVerb); } else { EngineLogger.error("SetCurrentScene - COULD NOT FIND SCENE: " + id); } diff --git a/blade-engine/src/com/bladecoder/engine/polygonalpathfinder/PolygonalNavGraph.java b/blade-engine/src/com/bladecoder/engine/polygonalpathfinder/PolygonalNavGraph.java index 036fe8fb0..dfc2aa112 100644 --- a/blade-engine/src/com/bladecoder/engine/polygonalpathfinder/PolygonalNavGraph.java +++ b/blade-engine/src/com/bladecoder/engine/polygonalpathfinder/PolygonalNavGraph.java @@ -42,17 +42,17 @@ public class PolygonalNavGraph implements NavGraph { private static final Vector2 tmp2 = new Vector2(); private Polygon walkZone; - private final ArrayList obstacles = new ArrayList(); + private final ArrayList obstacles = new ArrayList<>(); - final private PathFinder pathfinder = new AStarPathFinder(this, + final private PathFinder pathfinder = new AStarPathFinder<>(this, MAX_PATHFINDER_SEARCH_DISTANCE, new ManhattanDistance()); - final private NavPathPolygonal resultPath = new NavPathPolygonal(); + final private NavNodePolygonal startNode = new NavNodePolygonal(); final private NavNodePolygonal targetNode = new NavNodePolygonal(); - final private ArrayList graphNodes = new ArrayList(); + final private ArrayList graphNodes = new ArrayList<>(); public ArrayList findPath(float sx, float sy, float tx, float ty) { - resultPath.clear(); + final NavPathPolygonal resultPath = new NavPathPolygonal(); Vector2 source = new Vector2(sx, sy); Vector2 target = new Vector2(tx, ty); @@ -125,10 +125,8 @@ public ArrayList findPath(float sx, float sy, float tx, float ty) { /** * Search the first polygon vertex inside the walkzone. * - * @param p - * the polygon - * @param target - * the vertex found + * @param p the polygon + * @param target the vertex found */ private void getFirstVertexInsideWalkzone(Polygon p, Vector2 target) { float verts[] = p.getTransformedVertices(); @@ -145,12 +143,12 @@ private void getFirstVertexInsideWalkzone(Polygon p, Vector2 target) { public void createInitialGraph(BaseActor wz, Collection actors) { graphNodes.clear(); - - if(wz == null) { + + if (wz == null) { walkZone = null; return; } - + walkZone = wz.getBBox(); // 1.- Add WalkZone convex nodes @@ -285,7 +283,7 @@ public void addDinamicObstacle(Polygon poly) { int idx = obstacles.indexOf(poly); // CHECK TO AVOID ADDING THE ACTOR SEVERAL TIMES - if (idx == -1) { + if (idx == -1 && walkZone != null) { obstacles.add(poly); addObstacleToGrapth(poly); } diff --git a/blade-engine/src/com/bladecoder/engine/serialization/ActionCallbackSerializer.java b/blade-engine/src/com/bladecoder/engine/serialization/ActionCallbackSerializer.java index 407806423..79b7de8e2 100644 --- a/blade-engine/src/com/bladecoder/engine/serialization/ActionCallbackSerializer.java +++ b/blade-engine/src/com/bladecoder/engine/serialization/ActionCallbackSerializer.java @@ -17,7 +17,7 @@ import com.bladecoder.engine.actions.Action; import com.bladecoder.engine.actions.ActionCallback; -import com.bladecoder.engine.ink.InkManager; +import com.bladecoder.engine.ink.InkManager.InkVerbRunner; import com.bladecoder.engine.model.BaseActor; import com.bladecoder.engine.model.InteractiveActor; import com.bladecoder.engine.model.Inventory; @@ -115,12 +115,18 @@ private static String find(ActionCallback cb, Scene s) { return null; } - private static String find(ActionCallback cb, InkManager im) { + private static String find(ActionCallback cb, InkVerbRunner im) { if (im == null) return null; - if (cb instanceof InkManager) - return INK_MANAGER_TAG; + if (cb instanceof InkVerbRunner) { + if (cb == im) { + return INK_MANAGER_TAG; + } else { + EngineLogger.debug("CB pointing to an old InkVerbRunner. IGNORING."); + return null; + } + } int pos = 0; @@ -175,11 +181,10 @@ private static String find(ActionCallback cb, Inventory inv) { /** * Generates a String for serialization that allows locate the ActionCallback * - * @param cb - * The ActionCallback to serialize + * @param cb The ActionCallback to serialize * @return The generated location string */ - public static String find(World w, ActionCallback cb) { + public static String find(World w, Scene s, ActionCallback cb) { String id = null; if (cb == null) @@ -198,14 +203,14 @@ public static String find(World w, ActionCallback cb) { return id; // search in inkManager actions - id = find(cb, w.getInkManager()); + if (w.getInkManager() != null) { + id = find(cb, w.getInkManager().getVerbRunner()); + } if (id != null) return id; // search in scene verbs - Scene s = w.getCurrentScene(); - id = find(cb, s); if (id != null) @@ -245,21 +250,19 @@ public static String find(World w, ActionCallback cb) { * * @param id */ - public static ActionCallback find(World w, String id) { + public static ActionCallback find(World w, Scene s, String id) { if (id == null) return null; - Scene s = w.getCurrentScene(); - String[] split = id.split(SEPARATION_SYMBOL); if (id.startsWith(INK_MANAGER_TAG)) { if (split.length == 1) - return w.getInkManager(); + return w.getInkManager().getVerbRunner(); int actionPos = Integer.parseInt(split[1]); - Action action = w.getInkManager().getActions().get(actionPos); + Action action = w.getInkManager().getVerbRunner().getActions().get(actionPos); if (action instanceof ActionCallback) return (ActionCallback) action; diff --git a/blade-engine/src/com/bladecoder/engine/serialization/BladeJson.java b/blade-engine/src/com/bladecoder/engine/serialization/BladeJson.java index 04cf390d8..9fd280883 100644 --- a/blade-engine/src/com/bladecoder/engine/serialization/BladeJson.java +++ b/blade-engine/src/com/bladecoder/engine/serialization/BladeJson.java @@ -1,6 +1,23 @@ package com.bladecoder.engine.serialization; import com.badlogic.gdx.utils.Json; +import com.badlogic.gdx.utils.ObjectMap; +import com.bladecoder.engine.actions.Action; +import com.bladecoder.engine.actions.ActionFactory; +import com.bladecoder.engine.anim.AtlasAnimationDesc; +import com.bladecoder.engine.anim.SpineAnimationDesc; +import com.bladecoder.engine.model.AnchorActor; +import com.bladecoder.engine.model.AtlasRenderer; +import com.bladecoder.engine.model.CharacterActor; +import com.bladecoder.engine.model.ImageRenderer; +import com.bladecoder.engine.model.InteractiveActor; +import com.bladecoder.engine.model.ObstacleActor; +import com.bladecoder.engine.model.ParticleRenderer; +import com.bladecoder.engine.model.Scene; +import com.bladecoder.engine.model.Sprite3DRenderer; +import com.bladecoder.engine.model.SpriteActor; +import com.bladecoder.engine.model.TextRenderer; +import com.bladecoder.engine.model.WalkZoneActor; import com.bladecoder.engine.model.World; /** @@ -18,12 +35,39 @@ public enum Mode { private final Mode mode; private boolean init; + // the scene being saved + private Scene scene; + public BladeJson(World w, Mode mode, boolean init) { super(); this.w = w; this.mode = mode; this.init = init; + + // Add tags for known classes to reduce .json size. + addClassTag(SpineAnimationDesc.class); + addClassTag(AtlasAnimationDesc.class); + + addClassTag(CharacterActor.class); + addClassTag(AnchorActor.class); + addClassTag(ObstacleActor.class); + addClassTag(InteractiveActor.class); + addClassTag(SpriteActor.class); + addClassTag(WalkZoneActor.class); + + addClassTag(AtlasRenderer.class); + addClassTag(ImageRenderer.class); + addClassTag(ParticleRenderer.class); + addClassTag(Sprite3DRenderer.class); + addClassTag(TextRenderer.class); + + ObjectMap> classTags = ActionFactory.getClassTags(); + + for (ObjectMap.Entry> e : classTags.entries()) { + addClassTag(e.key, e.value); + } + } public BladeJson(World w, Mode mode) { @@ -45,4 +89,16 @@ public boolean getInit() { public void setInit(boolean init) { this.init = init; } + + public Scene getScene() { + return scene == null ? w.getCurrentScene() : scene; + } + + public void setScene(Scene scene) { + this.scene = scene; + } + + public void addClassTag(Class tag) { + addClassTag(tag.getSimpleName(), tag); + } } diff --git a/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java b/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java index 17f32af4b..5e935737c 100644 --- a/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java +++ b/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java @@ -6,6 +6,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; @@ -21,7 +23,6 @@ import com.bladecoder.engine.actions.SoundAction; import com.bladecoder.engine.anim.AnimationDesc; import com.bladecoder.engine.assets.EngineAssetManager; -import com.bladecoder.engine.i18n.I18N; import com.bladecoder.engine.model.AnimationRenderer; import com.bladecoder.engine.model.BaseActor; import com.bladecoder.engine.model.CharacterActor; @@ -40,7 +41,7 @@ @SuppressWarnings("deprecation") public class WorldSerialization implements Serializable { - public static final String GAMESTATE_EXT = ".gamestate.v13"; + public static final String GAMESTATE_EXT = ".gamestate.v14"; private static final int SCREENSHOT_DEFAULT_WIDTH = 300; @@ -88,7 +89,7 @@ public void loadWorldDesc() throws IOException { w.setHeight((int) (height * scale)); w.setInitChapter(json.readValue("initChapter", String.class, root)); w.getVerbManager().read(json, root); - I18N.loadWorld(EngineAssetManager.MODEL_DIR + EngineAssetManager.WORLD_FILENAME); + w.getI18N().loadWorld(EngineAssetManager.MODEL_DIR + EngineAssetManager.WORLD_FILENAME); } public void saveWorldDesc(FileHandle file) throws IOException { @@ -154,11 +155,11 @@ public void loadChapter(String chapterName, String scene, boolean initScene) thr read(json, root); if (scene == null) - w.setCurrentScene(w.getScenes().get(w.getInitScene()), initScene); + w.setCurrentScene(w.getScenes().get(w.getInitScene()), initScene, null); else - w.setCurrentScene(w.getScenes().get(scene), initScene); + w.setCurrentScene(w.getScenes().get(scene), initScene, null); - I18N.loadChapter(EngineAssetManager.MODEL_DIR + chapterName); + w.getI18N().loadChapter(EngineAssetManager.MODEL_DIR + chapterName); w.getCustomProperties().put(WorldProperties.CURRENT_CHAPTER.toString(), chapterName); w.getCustomProperties().put(WorldProperties.PLATFORM.toString(), Gdx.app.getType().toString()); @@ -227,6 +228,7 @@ public void saveGameState(String filename, boolean screenshot) throws IOExceptio Json json = new BladeJson(w, Mode.STATE); json.setOutputType(OutputType.javascript); + json.setSortFields(true); String s = null; @@ -255,16 +257,27 @@ public void saveGameState(String filename, boolean screenshot) throws IOExceptio public void write(Json json) { BladeJson bjson = (BladeJson) json; - json.writeValue(Config.BLADE_ENGINE_VERSION_PROP, Config.getProperty(Config.BLADE_ENGINE_VERSION_PROP, null)); + json.writeValue(Config.BLADE_ENGINE_VERSION_PROP, + Config.getInstance().getProperty(Config.BLADE_ENGINE_VERSION_PROP, null)); if (bjson.getMode() == Mode.MODEL) { - json.writeValue("sounds", w.getSounds(), w.getSounds().getClass(), SoundDesc.class); - json.writeValue("scenes", w.getScenes(), w.getScenes().getClass(), Scene.class); + SortedMap sortedSounds = new TreeMap<>(); + sortedSounds.putAll(w.getSounds()); + json.writeValue("sounds", sortedSounds, sortedSounds.getClass(), SoundDesc.class); + + SortedMap sortedScenes = new TreeMap<>(); + sortedScenes.putAll(w.getScenes()); + json.writeValue("scenes", sortedScenes, sortedScenes.getClass(), Scene.class); + json.writeValue("initScene", w.getInitScene()); } else { - json.writeValue(Config.VERSION_PROP, Config.getProperty(Config.VERSION_PROP, null)); - json.writeValue("scenes", w.getScenes(), w.getScenes().getClass(), Scene.class); + json.writeValue(Config.VERSION_PROP, Config.getInstance().getProperty(Config.VERSION_PROP, null)); + + SortedMap sortedScenes = new TreeMap<>(); + sortedScenes.putAll(w.getScenes()); + json.writeValue("scenes", sortedScenes, sortedScenes.getClass(), Scene.class); + json.writeValue("currentScene", w.getCurrentScene().getId()); json.writeValue("inventories", w.getInventories()); json.writeValue("currentInventory", w.getCurrentInventory()); @@ -301,9 +314,9 @@ public void read(Json json, JsonValue jsonData) { BladeJson bjson = (BladeJson) json; if (bjson.getMode() == Mode.MODEL) { if (bladeVersion != null - && !bladeVersion.equals(Config.getProperty(Config.BLADE_ENGINE_VERSION_PROP, ""))) { + && !bladeVersion.equals(Config.getInstance().getProperty(Config.BLADE_ENGINE_VERSION_PROP, ""))) { EngineLogger.debug("Model Engine Version v" + bladeVersion + " differs from Current Engine Version v" - + Config.getProperty(Config.BLADE_ENGINE_VERSION_PROP, "")); + + Config.getInstance().getProperty(Config.BLADE_ENGINE_VERSION_PROP, "")); } // SOUNDS @@ -348,10 +361,10 @@ public void read(Json json, JsonValue jsonData) { cacheSounds(); } else { if (bladeVersion != null - && !bladeVersion.equals(Config.getProperty(Config.BLADE_ENGINE_VERSION_PROP, ""))) { + && !bladeVersion.equals(Config.getInstance().getProperty(Config.BLADE_ENGINE_VERSION_PROP, ""))) { EngineLogger .debug("Saved Game Engine Version v" + bladeVersion + " differs from Current Engine Version v" - + Config.getProperty(Config.BLADE_ENGINE_VERSION_PROP, "")); + + Config.getInstance().getProperty(Config.BLADE_ENGINE_VERSION_PROP, "")); } String currentChapter = json.readValue("chapter", String.class, jsonData); @@ -426,7 +439,7 @@ public void read(Json json, JsonValue jsonData) { w.getTransition().read(json, jsonData.get("transition")); w.getMusicManager().read(json, jsonData.get("musicEngine")); - I18N.loadChapter(EngineAssetManager.MODEL_DIR + w.getCurrentChapter()); + w.getI18N().loadChapter(EngineAssetManager.MODEL_DIR + w.getCurrentChapter()); } } @@ -449,16 +462,25 @@ private void cacheSounds() { String actor = ActionUtils.getStringValue(act, "actor"); String play = ActionUtils.getStringValue(act, "play"); if (play != null) { + + if (actor.equals("$PLAYER")) + actor = s.getPlayer().getId(); + SoundDesc sd = w.getSounds().get(actor + "_" + play); - if (sd != null) - s.getSoundManager().addSoundToLoad(sd); + if (sd == null) { + EngineLogger.error( + "Reference to sound not found: " + s.getId() + "." + actor + "." + play); + continue; + } + + s.getSoundManager().addSoundToLoad(sd); HashMap params = new HashMap<>(); params.put("sound", sd.getId()); try { - Action a2 = ActionFactory.createByClass(PlaySoundAction.class.getName(), params); + Action a2 = ActionFactory.create(PlaySoundAction.class.getName(), params); actions.set(i, a2); a2.init(w); } catch (ClassNotFoundException | ReflectionException e) { @@ -500,18 +522,26 @@ private void cacheSounds() { String actor = ActionUtils.getStringValue(act, "actor"); String play = ActionUtils.getStringValue(act, "play"); + if (play != null) { + if ("$PLAYER".equals(actor)) + actor = s.getPlayer().getId(); + SoundDesc sd = w.getSounds().get(actor + "_" + play); - if (sd != null) - s.getSoundManager().addSoundToLoad(sd); + if (sd == null) { + EngineLogger.error("Reference to sound not found: " + s.getId() + "." + + actor + "." + play); + continue; + } + + s.getSoundManager().addSoundToLoad(sd); HashMap params = new HashMap<>(); params.put("sound", sd.getId()); try { - Action a2 = ActionFactory.createByClass(PlaySoundAction.class.getName(), - params); + Action a2 = ActionFactory.create(PlaySoundAction.class.getName(), params); actions.set(i, a2); a2.init(w); } catch (ClassNotFoundException | ReflectionException e) { diff --git a/blade-engine/src/com/bladecoder/engine/ui/AnimButton.java b/blade-engine/src/com/bladecoder/engine/ui/AnimButton.java new file mode 100644 index 000000000..fa0f36986 --- /dev/null +++ b/blade-engine/src/com/bladecoder/engine/ui/AnimButton.java @@ -0,0 +1,29 @@ +package com.bladecoder.engine.ui; + +import com.badlogic.gdx.scenes.scene2d.ui.Button; +import com.badlogic.gdx.scenes.scene2d.ui.Skin; + +public class AnimButton extends Button { + public AnimButton(Skin skin, String styleName) { + super(skin, styleName); + } + + public AnimButton(ButtonStyle style) { + super(style); + } + + @Override + public void act(float delta) { + ButtonStyle style = getStyle(); + if (style.up != null && style.up instanceof AnimationDrawable) + ((AnimationDrawable) style.up).act(delta); + + if (style.over != null && style.over instanceof AnimationDrawable) + ((AnimationDrawable) style.over).act(delta); + + if (style.down != null && style.down instanceof AnimationDrawable) + ((AnimationDrawable) style.down).act(delta); + + super.act(delta); + } +} diff --git a/blade-engine/src/com/bladecoder/engine/ui/AnimationDrawable.java b/blade-engine/src/com/bladecoder/engine/ui/AnimationDrawable.java new file mode 100644 index 000000000..1e0cfe66e --- /dev/null +++ b/blade-engine/src/com/bladecoder/engine/ui/AnimationDrawable.java @@ -0,0 +1,75 @@ +package com.bladecoder.engine.ui; + +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.g2d.Animation; +import com.badlogic.gdx.graphics.g2d.Animation.PlayMode; +import com.badlogic.gdx.graphics.g2d.Batch; +import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion; +import com.badlogic.gdx.scenes.scene2d.utils.BaseDrawable; +import com.badlogic.gdx.scenes.scene2d.utils.Drawable; +import com.badlogic.gdx.scenes.scene2d.utils.TransformDrawable; + +public class AnimationDrawable extends BaseDrawable implements TransformDrawable { + public final Animation anim; + private float stateTime = 0; + private Color tint; + + public AnimationDrawable(Animation anim) { + this.anim = anim; + setMinWidth(anim.getKeyFrames()[0].getRegionWidth()); + setMinHeight(anim.getKeyFrames()[0].getRegionHeight()); + } + + public AnimationDrawable(AnimationDrawable ad) { + super(ad); + anim = new Animation(ad.getAnimation().getFrameDuration(), ad.getAnimation().getKeyFrames()); + anim.setPlayMode(PlayMode.LOOP); + } + + public void act(float delta) { + stateTime += delta; + } + + public void reset() { + stateTime = 0; + } + + @Override + public void draw(Batch batch, float x, float y, float width, float height) { + if (tint != null) + batch.setColor(tint); + + batch.draw(anim.getKeyFrame(stateTime), x, y, width, height); + + if (tint != null) + batch.setColor(Color.WHITE); + } + + @Override + public void draw(Batch batch, float x, float y, float originX, float originY, float width, float height, + float scaleX, float scaleY, float rotation) { + + if (tint != null) + batch.setColor(tint); + + batch.draw(anim.getKeyFrame(stateTime), x, y, originX, originY, width, height, scaleX, scaleY, rotation); + + if (tint != null) + batch.setColor(Color.WHITE); + } + + public Animation getAnimation() { + return anim; + } + + public Drawable tint(Color tint) { + AnimationDrawable d = new AnimationDrawable(this); + d.setTint(tint); + + return d; + } + + public void setTint(Color t) { + this.tint = t; + } +} diff --git a/blade-engine/src/com/bladecoder/engine/ui/BladeSkin.java b/blade-engine/src/com/bladecoder/engine/ui/BladeSkin.java index afbb006ff..612ec56d1 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/BladeSkin.java +++ b/blade-engine/src/com/bladecoder/engine/ui/BladeSkin.java @@ -3,13 +3,18 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.g2d.Animation; +import com.badlogic.gdx.graphics.g2d.Animation.PlayMode; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.TextureAtlas; +import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter; import com.badlogic.gdx.scenes.scene2d.ui.Skin; +import com.badlogic.gdx.scenes.scene2d.utils.BaseDrawable; import com.badlogic.gdx.scenes.scene2d.utils.Drawable; +import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Json; import com.badlogic.gdx.utils.Json.ReadOnlySerializer; import com.badlogic.gdx.utils.JsonValue; @@ -111,6 +116,7 @@ public BitmapFont read(Json json, JsonValue jsonData, @SuppressWarnings("rawtype FreeTypeFontGenerator generator = new FreeTypeFontGenerator(fontFile); FreeTypeFontParameter parameter = new FreeTypeFontParameter(); parameter.size = (int) (DPIUtils.dpToPixels(size) * DPIUtils.getSizeMultiplier()); + parameter.color = json.readValue("color", Color.class, Color.WHITE, jsonData); parameter.incremental = json.readValue("incremental", boolean.class, true, jsonData); parameter.borderWidth = json.readValue("borderWidth", int.class, 0, jsonData); parameter.borderColor = json.readValue("borderColor", Color.class, Color.BLACK, jsonData); @@ -166,10 +172,63 @@ else if (size != -1) // TODO set size in points (dpi } }); + json.setSerializer(AnimationDrawable.class, new ReadOnlySerializer() { + @Override + public AnimationDrawable read(Json json, JsonValue jsonData, @SuppressWarnings("rawtypes") Class type) { + String name = json.readValue("name", String.class, jsonData); + float duration = json.readValue("duration", Float.class, 1f, jsonData); + PlayMode playMode = json.readValue("play_mode", PlayMode.class, PlayMode.LOOP, jsonData); + + Array regions = getAtlas().findRegions(name); + + if (regions.size == 0) + throw new SerializationException("AnimationDrawable not found: " + name); + + Animation a = new Animation<>(duration / regions.size, regions, playMode); + AnimationDrawable drawable = new AnimationDrawable(a); + + if (drawable instanceof BaseDrawable) { + BaseDrawable named = drawable; + named.setName(jsonData.name + " (" + name + ", " + duration + ")"); + } + + return drawable; + } + }); + + json.addClassTag("AnimationDrawable", AnimationDrawable.class); + return json; } public void addStyleTag(Class tag) { getJsonClassTags().put(tag.getSimpleName(), tag); } + + @Override + public Drawable newDrawable(Drawable drawable) { + if (drawable instanceof AnimationDrawable) + return new AnimationDrawable((AnimationDrawable) drawable); + return super.newDrawable(drawable); + } + + @Override + public Drawable newDrawable(Drawable drawable, Color tint) { + Drawable newDrawable; + if (drawable instanceof AnimationDrawable) { + newDrawable = ((AnimationDrawable) drawable).tint(tint); + + if (newDrawable instanceof BaseDrawable) { + BaseDrawable named = (BaseDrawable) newDrawable; + if (drawable instanceof BaseDrawable) + named.setName(((BaseDrawable) drawable).getName() + " (" + tint + ")"); + else + named.setName(" (" + tint + ")"); + } + + return newDrawable; + } + + return super.newDrawable(drawable, tint); + } } diff --git a/blade-engine/src/com/bladecoder/engine/ui/CreditsScreen.java b/blade-engine/src/com/bladecoder/engine/ui/CreditsScreen.java index bd80e0cf8..b13354913 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/CreditsScreen.java +++ b/blade-engine/src/com/bladecoder/engine/ui/CreditsScreen.java @@ -23,12 +23,14 @@ import java.util.Locale; import java.util.Map; +import com.badlogic.gdx.Application.ApplicationType; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input; import com.badlogic.gdx.InputAdapter; import com.badlogic.gdx.InputProcessor; import com.badlogic.gdx.ScreenAdapter; import com.badlogic.gdx.audio.Music; +import com.badlogic.gdx.controllers.Controller; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.Texture; @@ -42,6 +44,7 @@ import com.badlogic.gdx.utils.viewport.Viewport; import com.bladecoder.engine.assets.EngineAssetManager; import com.bladecoder.engine.ui.UI.Screens; +import com.bladecoder.engine.ui.defaults.ScreenControllerHandler; import com.bladecoder.engine.util.DPIUtils; import com.bladecoder.engine.util.EngineLogger; @@ -67,11 +70,12 @@ public class CreditsScreen extends ScreenAdapter implements BladeScreen { private final GlyphLayout layout = new GlyphLayout(); + private ScreenControllerHandler controller; + private final InputProcessor inputProcessor = new InputAdapter() { @Override public boolean keyUp(int keycode) { - if (keycode == Input.Keys.ESCAPE - || keycode == Input.Keys.BACK) + if (keycode == Input.Keys.ESCAPE || keycode == Input.Keys.BACK) ui.setCurrentScreen(Screens.MENU_SCREEN); return true; @@ -110,7 +114,8 @@ public void render(float delta) { for (int i = stringHead; i < credits.size(); i++) { String s = credits.get(i); - char type = 'c'; // types are 'c' -> credit, 't' -> title, 'i' -> image, 's' -> space, 'm' -> music + char type = 'c'; // types are 'c' -> credit, 't' -> title, 'i' -> image, 's' -> space, 'm' -> + // music if (s.indexOf('#') != -1) { type = s.charAt(0); @@ -118,23 +123,23 @@ public void render(float delta) { } switch (type) { - case 't': - y = processCreditTitle(batch, width, height, y, i, s); - break; - case 'i': - y = processCreditImage(batch, width, height, y, i, s); - break; - case 's': - y = processCreditSpace(height, y, i, s); - break; - case 'm': - processCreditMusic(s); - credits.remove(i); - i--; - break; - default: - y = processCreditDefault(batch, width, height, y, i, s); - break; + case 't': + y = processCreditTitle(batch, width, height, y, i, s); + break; + case 'i': + y = processCreditImage(batch, width, height, y, i, s); + break; + case 's': + y = processCreditSpace(height, y, i, s); + break; + case 'm': + processCreditMusic(s); + credits.remove(i); + i--; + break; + default: + y = processCreditDefault(batch, width, height, y, i, s); + break; } if (y < 0) { @@ -143,6 +148,8 @@ public void render(float delta) { } batch.end(); + + controller.update(delta); } private float processCreditTitle(SpriteBatch batch, int width, int height, float y, int i, String s) { @@ -188,22 +195,34 @@ private float processCreditSpace(int height, float y, int i, String s) { private void processCreditMusic(final String s) { if (music != null) music.dispose(); - - final String sound = EngineAssetManager.getInstance().checkIOSSoundName("music/" + s); + + final String file = EngineAssetManager.getInstance().checkIOSSoundName("music/" + s); new Thread() { @Override public void run() { - music = Gdx.audio.newMusic(EngineAssetManager.getInstance().getAsset(sound)); - + music = Gdx.audio.newMusic(EngineAssetManager.getInstance().getAsset(file)); + try { music.play(); - } catch(Exception e) { - // sometimes the play method fails on desktop. - EngineLogger.error("Error Playing music: " + s, e); + music.setVolume(0.5f); + } catch (Exception e) { + + // DEAL WITH OPENAL BUG + if (Gdx.app.getType() == ApplicationType.Desktop && e.getMessage().contains("40963")) { + EngineLogger.debug("!!!!!!!!!!!!!!!!!!!!!!!ERROR playing music trying again...!!!!!!!!!!!!!!!"); + + music = Gdx.audio.newMusic(EngineAssetManager.getInstance().getAsset(file)); + music.play(); + music.setVolume(0.5f); + + return; + } + + EngineLogger.error("Error Playing music: " + file); } } - }.start(); + }.start(); } private float processCreditDefault(SpriteBatch batch, int width, int height, float y, int i, String s) { @@ -290,6 +309,16 @@ public void show() { stringHead = 0; scrollY = 0; + + controller = new ScreenControllerHandler(ui, null, viewport) { + @Override + protected boolean buttonUp(Controller controller, int buttonCode) { + + ui.setCurrentScreen(Screens.MENU_SCREEN); + + return true; + } + }; } @Override @@ -307,7 +336,7 @@ public void drawCenteredScreenX(SpriteBatch batch, BitmapFont font, CharSequence layout.setText(font, str, Color.WHITE, viewportWidth, Align.center, true); - //x = (viewportWidth - layout.width)/2; + // x = (viewportWidth - layout.width)/2; font.draw(batch, layout, x, y); } diff --git a/blade-engine/src/com/bladecoder/engine/ui/DebugDrawer.java b/blade-engine/src/com/bladecoder/engine/ui/DebugDrawer.java new file mode 100644 index 000000000..5cbee2938 --- /dev/null +++ b/blade-engine/src/com/bladecoder/engine/ui/DebugDrawer.java @@ -0,0 +1,122 @@ +package com.bladecoder.engine.ui; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.g2d.GlyphLayout; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.math.Rectangle; +import com.badlogic.gdx.math.Vector3; +import com.badlogic.gdx.scenes.scene2d.ui.Skin; +import com.badlogic.gdx.utils.Align; +import com.badlogic.gdx.utils.viewport.Viewport; +import com.bladecoder.engine.model.AnchorActor; +import com.bladecoder.engine.model.BaseActor; +import com.bladecoder.engine.model.InteractiveActor; +import com.bladecoder.engine.model.World; +import com.bladecoder.engine.util.EngineLogger; +import com.bladecoder.engine.util.RectangleRenderer; + +public class DebugDrawer { + + private final Skin skin; + private final World w; + private final Viewport viewport; + private final StringBuilder sbTmp = new StringBuilder(); + private final Vector3 unprojectTmp = new Vector3(); + private final GlyphLayout textLayout = new GlyphLayout(); + + public DebugDrawer(World w, Skin skin, Viewport viewport) { + this.w = w; + this.skin = skin; + this.viewport = viewport; + } + + public void draw(SpriteBatch batch) { + + w.getSceneCamera().getInputUnProject(viewport, unprojectTmp); + + Color color; + + sbTmp.setLength(0); + + if (EngineLogger.lastError != null) { + // sbTmp.append(EngineLogger.lastError); + sbTmp.append(EngineLogger.errorBuffer); + + color = Color.RED; + } else { + + // sbTmp.append(" Density:"); + // sbTmp.append(Gdx.graphics.getDensity()); + // sbTmp.append(" UI Multiplier:"); + // sbTmp.append(DPIUtils.getSizeMultiplier()); + sbTmp.append(" "); + + long millis = w.getTimeOfGame(); + long second = (millis / 1000) % 60; + long minute = (millis / (1000 * 60)) % 60; + long hour = (millis / (1000 * 60 * 60)); + + String time = String.format("%02d:%02d:%02d", hour, minute, second); + + sbTmp.append(time); + + if (EngineLogger.getDebugLevel() == EngineLogger.DEBUG1) { + if (w.inCutMode()) { + sbTmp.append(" CUT_MODE "); + } else if (w.hasDialogOptions()) { + sbTmp.append(" DIALOG_MODE "); + } else if (w.isPaused()) { + sbTmp.append(" PAUSED "); + } + + sbTmp.append(" ( "); + sbTmp.append((int) unprojectTmp.x); + sbTmp.append(", "); + sbTmp.append((int) unprojectTmp.y); + sbTmp.append(") FPS:"); + sbTmp.append(Gdx.graphics.getFramesPerSecond()); + + if (w.getCurrentScene().getState() != null) { + sbTmp.append(" Scn State: "); + sbTmp.append(w.getCurrentScene().getState()); + } + + if (w.getCurrentScene().getPlayer() != null) { + sbTmp.append(" Depth Scl: "); + sbTmp.append(w.getCurrentScene().getFakeDepthScale(unprojectTmp.y)); + } + } + + color = Color.WHITE; + } + + String strDebug = sbTmp.toString(); + + textLayout.setText(skin.getFont("debug"), strDebug, color, viewport.getScreenWidth(), Align.left, true); + RectangleRenderer.draw(batch, 0, viewport.getScreenHeight() - textLayout.height - 10, textLayout.width, + textLayout.height + 10, Color.BLACK); + skin.getFont("debug").draw(batch, textLayout, 0, viewport.getScreenHeight() - 5); + + // Draw actor states when debug + if (EngineLogger.getDebugLevel() == EngineLogger.DEBUG1) { + + for (BaseActor a : w.getCurrentScene().getActors().values()) { + + if (a instanceof AnchorActor) + continue; + + Rectangle r = a.getBBox().getBoundingRectangle(); + sbTmp.setLength(0); + sbTmp.append(a.getId()); + if (a instanceof InteractiveActor && ((InteractiveActor) a).getState() != null) + sbTmp.append(".").append(((InteractiveActor) a).getState()); + + unprojectTmp.set(r.getX(), r.getY(), 0); + w.getSceneCamera().scene2screen(viewport, unprojectTmp); + skin.getFont("debug").draw(batch, sbTmp.toString(), unprojectTmp.x, unprojectTmp.y); + } + + } + } +} diff --git a/blade-engine/src/com/bladecoder/engine/ui/DebugScreen.java b/blade-engine/src/com/bladecoder/engine/ui/DebugScreen.java index 5ff40a6f4..ca8328b61 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/DebugScreen.java +++ b/blade-engine/src/com/bladecoder/engine/ui/DebugScreen.java @@ -40,6 +40,7 @@ import com.badlogic.gdx.utils.viewport.ScreenViewport; import com.bladecoder.engine.assets.EngineAssetManager; import com.bladecoder.engine.ui.UI.Screens; +import com.bladecoder.engine.ui.defaults.ScreenControllerHandler; import com.bladecoder.engine.util.Config; import com.bladecoder.engine.util.DPIUtils; import com.bladecoder.engine.util.EngineLogger; @@ -61,6 +62,8 @@ public class DebugScreen implements BladeScreen { private Pointer pointer; + private ScreenControllerHandler controller; + public DebugScreen() { } @@ -71,6 +74,8 @@ public void render(float delta) { stage.act(delta); stage.draw(); + + controller.update(delta); } @Override @@ -112,10 +117,12 @@ public boolean keyUp(InputEvent event, int keycode) { }); stage.setKeyboardFocus(table); + controller = new ScreenControllerHandler(ui, stage, stage.getViewport()); Button back = new Button(ui.getSkin(), "back"); back.addListener(new ClickListener() { + @Override public void clicked(InputEvent event, float x, float y) { ui.setCurrentScreen(Screens.SCENE_SCREEN); } @@ -125,7 +132,7 @@ public void clicked(InputEvent event, float x, float y) { Table header = new Table(); header.padBottom(margin); - Container