From f4c340dca5818e98f1318911741fa5fb3afd79cf Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Mon, 22 Apr 2019 10:53:42 +0200 Subject: [PATCH 001/147] Update README.md --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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. From 154ce30f785734780271b9fa6058e1ab08ec5836 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Fri, 26 Apr 2019 14:13:54 +0200 Subject: [PATCH 002/147] Better info in verb list subtitle. --- .../bladecoder/engineeditor/ui/VerbList.java | 43 ++++++++----------- 1 file changed, 18 insertions(+), 25 deletions(-) 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(); } From 4f7ac064c87d1f148202356843a95c0979a62e2d Mon Sep 17 00:00:00 2001 From: rgarcia Date: Fri, 26 Apr 2019 14:14:29 +0200 Subject: [PATCH 003/147] Added TODO: New libgdx field. Waiting for release --- .../engine/serialization/WorldSerialization.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java b/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java index 17f32af4b..9f94237ab 100644 --- a/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java +++ b/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java @@ -126,10 +126,12 @@ public void loadChapter() throws IOException { /** * Loads a JSON chapter file. * - * @param chapterName filename without path and extension. - * @param scene the init scene. null to use the chapter defined init - * scene. - * @param initScene false only when it comes from loading a saved game. + * @param chapterName + * filename without path and extension. + * @param scene + * the init scene. null to use the chapter defined init scene. + * @param initScene + * false only when it comes from loading a saved game. * @throws IOException */ public void loadChapter(String chapterName, String scene, boolean initScene) throws IOException { @@ -181,6 +183,8 @@ public void saveModel(String chapterId) throws IOException { Json json = new BladeJson(w, Mode.MODEL); json.setOutputType(OutputType.javascript); + // TODO: New libgdx field. Waiting for release! + // json.setSortFields(true); String s = null; From 666e283b4c7031006f5bf66e870e62ffda9e2cf1 Mon Sep 17 00:00:00 2001 From: danigm Date: Sat, 27 Apr 2019 15:15:38 +0200 Subject: [PATCH 004/147] Fix svg desktop icon (#44) --- .../com.bladecoder.adventure-editor.svg | 62 ++++++------------- 1 file changed, 19 insertions(+), 43 deletions(-) diff --git a/adventure-editor/desktop/com.bladecoder.adventure-editor.svg b/adventure-editor/desktop/com.bladecoder.adventure-editor.svg index 04fc0b027..d423aaf8c 100644 --- a/adventure-editor/desktop/com.bladecoder.adventure-editor.svg +++ b/adventure-editor/desktop/com.bladecoder.adventure-editor.svg @@ -7,54 +7,41 @@ 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="com.bladecoder.adventure-editor.svg" inkscape:export-filename="ic_app16.png" inkscape:export-xdpi="11.25" inkscape:export-ydpi="11.25"> - - - - + 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" /> Date: Sat, 27 Apr 2019 19:56:27 +0200 Subject: [PATCH 005/147] Added reload assets icon to scene list. --- .../assets-raw/editor/icons-atlas.svg | 56 +++++++++++++++--- .../editor/icons/ic_reload_small.png | Bin 0 -> 609 bytes .../editor/icons/ic_reload_small_disabled.png | Bin 0 -> 894 bytes .../editor/{icons/gen.sh => pack_icons.sh} | 9 ++- .../bladecoder/engineeditor/ui/SceneList.java | 19 ++++++ .../src/main/resources/images/icons.atlas | 34 +++++++---- .../src/main/resources/images/icons.png | Bin 43472 -> 44486 bytes .../PolygonalNavGraph.java | 16 +++-- 8 files changed, 103 insertions(+), 31 deletions(-) create mode 100644 adventure-editor/assets-raw/editor/icons/ic_reload_small.png create mode 100644 adventure-editor/assets-raw/editor/icons/ic_reload_small_disabled.png rename adventure-editor/assets-raw/editor/{icons/gen.sh => pack_icons.sh} (50%) diff --git a/adventure-editor/assets-raw/editor/icons-atlas.svg b/adventure-editor/assets-raw/editor/icons-atlas.svg index ab1cc1ade..7f4707567 100644 --- a/adventure-editor/assets-raw/editor/icons-atlas.svg +++ b/adventure-editor/assets-raw/editor/icons-atlas.svg @@ -14,7 +14,7 @@ height="2373.0876" id="svg2" version="1.1" - inkscape:version="0.92.3 (2405546, 2018-03-11)" + inkscape:version="0.92.4 (5da689c313, 2019-01-14)" sodipodi:docname="icons-atlas.svg" inkscape:export-filename="/home/rgarcia/WORKSPACES/ENGINE/engine-editor/src/res/images/ic_goto.png" inkscape:export-xdpi="22.5" @@ -27,17 +27,17 @@ borderopacity="1.0" inkscape:pageopacity="0" inkscape:pageshadow="2" - inkscape:zoom="0.70627929" - inkscape:cx="2258.6787" - inkscape:cy="874.77137" + inkscape:zoom="0.99882975" + inkscape:cx="2631.1354" + inkscape:cy="529.29897" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="false" showborder="true" borderlayer="true" - inkscape:window-width="1853" - inkscape:window-height="1025" - inkscape:window-x="67" + inkscape:window-width="1850" + inkscape:window-height="1016" + inkscape:window-x="70" inkscape:window-y="27" inkscape:window-maximized="1" inkscape:snap-global="false" @@ -4651,7 +4651,7 @@ inkscape:export-xdpi="18.281246" inkscape:export-filename="icons/ic_player_small.png" id="g3991-8" - transform="translate(1920.8676,1543.8758)"> + 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 0000000000000000000000000000000000000000..549a03cc285a8357e03ecc6a27f1b664eaefc945 GIT binary patch literal 609 zcmV-n0-pVeP)|jd31TX^}1aqht1 z)_$g&!~VDR0#ZsVMZm|fKe}E(t{m_-tRMSV007i;;TbHd+j`8nuRc*{&4zZkxVc09 z5+FBw2tQRa2WO+{N{MM!?duSJM*SY@7TW66t?HW+$W!&CIvD&DW)r;{{7v;})oJ#t zpGzRBE9wig_x@sqNA*Idejc!W@Ve0t^?Yr30kTuQW&PW-sCo5pSK$FPrd~69Xi*LI vu6ja^e_X$ndVp;Lb^%*}rIb>f{!;w{x-fMTvA6sv00000NkvXXu0mjf%p(q) literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..f88e20a35fc003c688203f66fbc1eab3c0b35988 GIT binary patch literal 894 zcmV-^1A+XBP)V35C>2ghReS`(Ru_yWRdCSc&Jt zFf2$~02VHNkcT8)7Y7_jbTI1N$A)w63~&ZG6@%vBpJ$p)0Qy%07z6!14mg_t9Q?O{ zo!9|<7f+hm{r?p}06`EOlk`R`A7!)In;MPAZ&xZ51i||Z-L`FJZAqUy=YB~6q|@nD zGmBepzTfY^e5K-H7(OcL5lQy|&q`Va9)zLm(d+g4#bPlFRAO~^6$*u1xm^CN+wIyo z@oKgD81OcG|1IC-^ZDc5Zr64{^Tox*xlAV00d7ucd@1RL+1c6kMx*gp>`2OzPMF!l zlH#VhEh%U=n_ob}N2=B8t!8!_*gs;ke=vOOe*!lG`B5F2+0(67>$RQnWk$8Mv~=h8 z_V$NBag@M>xClJsoO?Z2&(QU@*8A_{lkUc4`suH~$p} U{e6#bM*si-07*qoM6N<$f?iIYBme*a literal 0 HcmV?d00001 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/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/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 4705d447ea55a8ec416838d14a6860f6293374b8..6aa6ac77d4917862f57770606c5996ca5e507432 100644 GIT binary patch literal 44486 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 z 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 literal 43472 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 z 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 be`itYaXNLGHw$aG0Q@1IG1e>Bv5)*8&BVNg diff --git a/blade-engine/src/com/bladecoder/engine/polygonalpathfinder/PolygonalNavGraph.java b/blade-engine/src/com/bladecoder/engine/polygonalpathfinder/PolygonalNavGraph.java index 036fe8fb0..40588a7c4 100644 --- a/blade-engine/src/com/bladecoder/engine/polygonalpathfinder/PolygonalNavGraph.java +++ b/blade-engine/src/com/bladecoder/engine/polygonalpathfinder/PolygonalNavGraph.java @@ -46,13 +46,13 @@ public class PolygonalNavGraph implements NavGraph { 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(); 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 From 4b1eb60e5dfd2d0a51303be3a2d0f5e8ee91bd8b Mon Sep 17 00:00:00 2001 From: rgarcia Date: Mon, 29 Apr 2019 14:12:10 +0200 Subject: [PATCH 006/147] Linear filtering for image renderer. --- .../src/com/bladecoder/engine/model/ImageRenderer.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/blade-engine/src/com/bladecoder/engine/model/ImageRenderer.java b/blade-engine/src/com/bladecoder/engine/model/ImageRenderer.java index c6dc0635a..f784519c4 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,6 +211,8 @@ private void retrieveSource(String source) { ((ImageCacheEntry) entry).tex = EngineAssetManager.getInstance() .getTexture(EngineAssetManager.IMAGE_DIR + source); + + ((ImageCacheEntry) entry).tex.setFilter(TextureFilter.Linear, TextureFilter.Linear); } } From 93bf5999b95276a33088ca5e64c7e2d44c2970df Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sat, 11 May 2019 21:46:03 +0200 Subject: [PATCH 007/147] Delete last_project key if loadproject fails so the editor doesn't fail forever. --- .../engineeditor/model/Project.java | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) 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..a27160c24 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; @@ -113,7 +113,7 @@ public class Project extends PropertyChange { public Project() { loadConfig(); } - + public World getWorld() { return world; } @@ -254,7 +254,7 @@ public String getTitle() { return projectConfig.getProperty(Config.TITLE_PROP, getProjectDir().getName()); } - + public boolean isLoaded() { return Ctx.project.getProjectDir() != null; } @@ -302,14 +302,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 +341,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 +353,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 +381,7 @@ 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).withOrdering().build(); projectConfig.load(new FileInputStream(getAssetPath() + "/" + Config.PROPERTIES_FILENAME)); modified = false; @@ -395,8 +398,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 +409,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 { @@ -509,13 +511,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 +541,7 @@ 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).withOrdering().build(); prop.load(new FileReader(projectPath.getAbsolutePath() + "/gradle.properties")); @@ -546,8 +549,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); } From 3d0709388af605a5348a937ab32afb0dcf246e21 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sat, 11 May 2019 21:46:20 +0200 Subject: [PATCH 008/147] Added RandomPosition action. --- .../engine/actions/RandomPositionAction.java | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 blade-engine/src/com/bladecoder/engine/actions/RandomPositionAction.java 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; + } + +} From 62041d382b8fa54f5033fe29ddff7cc55a0aa4fe Mon Sep 17 00:00:00 2001 From: rgarcia Date: Wed, 15 May 2019 19:30:08 +0200 Subject: [PATCH 009/147] Index out of bounds check. --- .../ui/panels/EditableSelectBox.java | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) 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(); From bfca9bc28ca2fca796b9771cda3336a7ca9c6118 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Thu, 16 May 2019 09:49:56 +0200 Subject: [PATCH 010/147] Label and text to warning about legacy dialogs. --- .../engineeditor/ui/ActorPanel.java | 117 +++++++++--------- .../engineeditor/ui/EditDialogDialog.java | 29 +++-- 2 files changed, 73 insertions(+), 73 deletions(-) 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/EditDialogDialog.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/EditDialogDialog.java index 796285ae6..8c28ac0ed 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 = "Actors can have several dialogs defined. Dialogs have a list of options to choose.\nThis 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()); - } + } } From 406226221fab2e6155ee40b9c139aa272d89aa92 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Thu, 16 May 2019 12:57:32 +0200 Subject: [PATCH 011/147] Create android keystore inside the editor. --- .../engineeditor/common/RunProccess.java | 18 +++ .../ui/CreateAndroidKeystoreDialog.java | 137 ++++++++++++++++++ .../engineeditor/ui/PackageDialog.java | 108 +++++++++----- 3 files changed, 225 insertions(+), 38 deletions(-) create mode 100644 adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/CreateAndroidKeystoreDialog.java 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/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/PackageDialog.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/ui/PackageDialog.java index ef0321858..16fb242cb 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 @@ -28,10 +28,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,7 +60,7 @@ 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" }; @@ -81,14 +86,14 @@ 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); @@ -137,7 +142,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,7 +155,7 @@ 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); @@ -213,14 +218,44 @@ public void changed(ChangeEvent event, Actor actor) { } }); + // 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 +280,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 +299,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,8 +321,8 @@ 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/"; @@ -327,10 +361,10 @@ private String packageAdv() throws IOException { } } } 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() + " "; + 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()) { @@ -341,8 +375,9 @@ private String packageAdv() throws IOException { 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) { + + 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."; } @@ -351,16 +386,14 @@ private String packageAdv() throws IOException { + "/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) + 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) { + + if (!newProjectStructure) { task = "android:assembleRelease"; - apk = Ctx.project.getProjectDir().getAbsolutePath() - + "/android/build/outputs/apk/android-release.apk"; + apk = Ctx.project.getProjectDir().getAbsolutePath() + "/android/build/outputs/apk/android-release.apk"; } if (genExpansion) { @@ -397,14 +430,14 @@ private String packageAdv() throws IOException { p.store(new FileOutputStream( new File(Ctx.project.getProjectDir().getAbsolutePath(), "/ios/robovm.properties")), null); - List params = new ArrayList(); + 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); @@ -454,7 +487,7 @@ private void archChanged() { setVisible(iosSignIdentity, true); setVisible(iosProvisioningProfile, true); } - + setVisible(customBuildParameters, true); } @@ -562,8 +595,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 +703,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; From 54fc03bb362e247ce62096cd74a51761f96a5588 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sun, 19 May 2019 12:58:42 +0200 Subject: [PATCH 012/147] Prepare for Blade Engie v.3.1.1 --- CHANGELOG.md | 13 +++++++++++++ .../src/main/resources/versions.properties | 2 +- gradle.properties | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b9f60d9d..af29a1b85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [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/adventure-editor/src/main/resources/versions.properties b/adventure-editor/src/main/resources/versions.properties index da89e7592..4df21f482 100644 --- a/adventure-editor/src/main/resources/versions.properties +++ b/adventure-editor/src/main/resources/versions.properties @@ -5,4 +5,4 @@ buildToolsVersion=28.0.3 libgdxVersion=1.9.9 roboVMGradlePluginVersion=2.3.5 roboVMVersion=2.3.5 -version=3.1.1-SNAPSHOT +version=3.1.1 diff --git a/gradle.properties b/gradle.properties index 51091c335..a8b2fc91d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=3.1.1-SNAPSHOT +version=3.1.1 libgdxVersion=1.9.9 roboVMVersion=2.3.5 roboVMGradlePluginVersion=2.3.5 From 62e93938c854477068ceff654cc8d9f329033f9a Mon Sep 17 00:00:00 2001 From: rgarcia Date: Thu, 23 May 2019 17:42:13 +0200 Subject: [PATCH 013/147] Added public methods to the SpineRenderer to get access to the current skeleton and animation. --- .../src/main/resources/versions.properties | 2 +- .../com/bladecoder/engine/spine/SpineRenderer.java | 10 ++++++++++ gradle.properties | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/adventure-editor/src/main/resources/versions.properties b/adventure-editor/src/main/resources/versions.properties index 4df21f482..f7c61fbe7 100644 --- a/adventure-editor/src/main/resources/versions.properties +++ b/adventure-editor/src/main/resources/versions.properties @@ -5,4 +5,4 @@ buildToolsVersion=28.0.3 libgdxVersion=1.9.9 roboVMGradlePluginVersion=2.3.5 roboVMVersion=2.3.5 -version=3.1.1 +version=3.1.2-SNAPSHOT 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..231c72f55 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 @@ -446,6 +446,16 @@ 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; diff --git a/gradle.properties b/gradle.properties index a8b2fc91d..dada79746 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=3.1.1 +version=3.1.2-SNAPSHOT libgdxVersion=1.9.9 roboVMVersion=2.3.5 roboVMGradlePluginVersion=2.3.5 From 3d5d8cc06227d7381f034192806568e0740a811d Mon Sep 17 00:00:00 2001 From: rgarcia Date: Wed, 29 May 2019 18:29:41 +0200 Subject: [PATCH 014/147] Allow empty properties in Property action. --- .../src/com/bladecoder/engine/actions/PropertyAction.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) 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; From b1e8fbf5eb240c06cb8ba77c9f89bea061d58363 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sun, 30 Jun 2019 23:23:26 +0200 Subject: [PATCH 015/147] Doesn't extract string expresions from ink. --- .../engineeditor/common/ModelTools.java | 4 ++ .../engine/ink/ExternalFunctions.java | 40 +++++++++++++++++++ 2 files changed, 44 insertions(+) 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..7c10736f2 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 @@ -499,6 +499,10 @@ 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")) + continue; + extractInkTextsInternal(aValue, sbTSV, sbMD, prop); } diff --git a/blade-engine/src/com/bladecoder/engine/ink/ExternalFunctions.java b/blade-engine/src/com/bladecoder/engine/ink/ExternalFunctions.java index 8e30fbcbf..a278fcef0 100644 --- a/blade-engine/src/com/bladecoder/engine/ink/ExternalFunctions.java +++ b/blade-engine/src/com/bladecoder/engine/ink/ExternalFunctions.java @@ -1,6 +1,10 @@ package com.bladecoder.engine.ink; +import com.bladecoder.engine.actions.SceneActorRef; +import com.bladecoder.engine.model.InteractiveActor; +import com.bladecoder.engine.model.Scene; import com.bladecoder.engine.model.World; +import com.bladecoder.engine.util.EngineLogger; import com.bladecoder.ink.runtime.Story.ExternalFunction; public class ExternalFunctions { @@ -26,5 +30,41 @@ public Object call(Object[] args) throws Exception { return w.getInventory().get(actor) != null; } }); + + inkManager.getStory().bindExternalFunction("getActorState", new ExternalFunction() { + + @Override + public Object call(Object[] args) throws Exception { + SceneActorRef actor = new SceneActorRef(args[0].toString()); + 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 ""; + } + + return a.getState() == null ? "" : a.getState(); + } + }); + + inkManager.getStory().bindExternalFunction("getSceneState", new ExternalFunction() { + + @Override + public Object call(Object[] args) throws Exception { + String scene = args[0].toString(); + final Scene s = w.getScene(scene); + + if (s == null) { + EngineLogger.error("getSceneState - Scene not found: " + scene); + return ""; + } + + return s.getState() == null ? "" : s.getState(); + } + }); } } From 38b9672ebef008fb4b7287035fa6cac83f444bdd Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sat, 6 Jul 2019 21:35:05 +0200 Subject: [PATCH 016/147] Support for animation UI buttons. --- .../com/bladecoder/engine/ui/AnimButton.java | 25 ++++++++ .../engine/ui/AnimationDrawable.java | 61 +++++++++++++++++++ .../com/bladecoder/engine/ui/BladeSkin.java | 54 ++++++++++++++++ .../src/com/bladecoder/engine/ui/PieMenu.java | 6 +- 4 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 blade-engine/src/com/bladecoder/engine/ui/AnimButton.java create mode 100644 blade-engine/src/com/bladecoder/engine/ui/AnimationDrawable.java 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..0dd4b1d47 --- /dev/null +++ b/blade-engine/src/com/bladecoder/engine/ui/AnimButton.java @@ -0,0 +1,25 @@ +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); + } + + @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..53ff80648 --- /dev/null +++ b/blade-engine/src/com/bladecoder/engine/ui/AnimationDrawable.java @@ -0,0 +1,61 @@ +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; + +class AnimationDrawable extends BaseDrawable { + 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); + } + + 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..465f4be47 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; @@ -166,10 +171,59 @@ 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 frameDuration = json.readValue("frame_duration", Float.class, jsonData); + + Array regions = getAtlas().findRegions(name); + + Animation a = new Animation(frameDuration, regions, PlayMode.LOOP); + AnimationDrawable drawable = new AnimationDrawable(a); + + if (drawable instanceof BaseDrawable) { + BaseDrawable named = drawable; + named.setName(jsonData.name + " (" + name + ", " + frameDuration + ")"); + } + + 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/PieMenu.java b/blade-engine/src/com/bladecoder/engine/ui/PieMenu.java index 9860e7911..05556d7ad 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/PieMenu.java +++ b/blade-engine/src/com/bladecoder/engine/ui/PieMenu.java @@ -52,7 +52,7 @@ public PieMenu(SceneScreen scr) { sceneScreen = scr; font = scr.getUI().getSkin().getFont("desc"); - lookatButton = new Button(scr.getUI().getSkin(), "pie_lookat"); + lookatButton = new AnimButton(scr.getUI().getSkin(), "pie_lookat"); addActor(lookatButton); lookatButton.addListener(new ChangeListener() { @Override @@ -65,7 +65,7 @@ public void changed(ChangeEvent event, com.badlogic.gdx.scenes.scene2d.Actor act } }); - talktoButton = new Button(scr.getUI().getSkin(), "pie_talkto"); + talktoButton = new AnimButton(scr.getUI().getSkin(), "pie_talkto"); addActor(talktoButton); talktoButton.addListener(new ChangeListener() { @Override @@ -78,7 +78,7 @@ public void changed(ChangeEvent event, com.badlogic.gdx.scenes.scene2d.Actor act } }); - pickupButton = new Button(scr.getUI().getSkin(), "pie_pickup"); + pickupButton = new AnimButton(scr.getUI().getSkin(), "pie_pickup"); addActor(pickupButton); pickupButton.addListener(new ChangeListener() { @Override From 1665f580c7e7c025051d8b41e4470899fec5c317 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sun, 7 Jul 2019 22:57:00 +0200 Subject: [PATCH 017/147] Support for animated mouse pointers. --- .../engine/ui/AnimationDrawable.java | 16 ++++++- .../src/com/bladecoder/engine/ui/Pointer.java | 16 ++++--- .../ui/defaults/DefaultSceneScreen.java | 23 ++++++--- .../engine/ui/defaults/ScenePointer.java | 47 +++++++++++++------ 4 files changed, 75 insertions(+), 27 deletions(-) diff --git a/blade-engine/src/com/bladecoder/engine/ui/AnimationDrawable.java b/blade-engine/src/com/bladecoder/engine/ui/AnimationDrawable.java index 53ff80648..1e0cfe66e 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/AnimationDrawable.java +++ b/blade-engine/src/com/bladecoder/engine/ui/AnimationDrawable.java @@ -7,8 +7,9 @@ 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; -class AnimationDrawable extends BaseDrawable { +public class AnimationDrawable extends BaseDrawable implements TransformDrawable { public final Animation anim; private float stateTime = 0; private Color tint; @@ -44,6 +45,19 @@ public void draw(Batch batch, float x, float y, float width, float height) { 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; } diff --git a/blade-engine/src/com/bladecoder/engine/ui/Pointer.java b/blade-engine/src/com/bladecoder/engine/ui/Pointer.java index 4c24f2568..0eefbe06a 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/Pointer.java +++ b/blade-engine/src/com/bladecoder/engine/ui/Pointer.java @@ -18,11 +18,11 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input.Peripheral; import com.badlogic.gdx.graphics.g2d.Batch; -import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.Touchable; import com.badlogic.gdx.scenes.scene2d.ui.Skin; +import com.badlogic.gdx.scenes.scene2d.utils.Drawable; import com.badlogic.gdx.utils.viewport.Viewport; import com.bladecoder.engine.util.DPIUtils; @@ -34,14 +34,14 @@ public class Pointer extends Actor { private static final String POINTER_ICON = "pointer"; - private TextureRegion pointerIcon; + private Drawable pointerIcon; private final Vector2 mousepos = new Vector2(); private float pointerScale; public Pointer(Skin skin) { - pointerIcon = skin.getAtlas().findRegion(POINTER_ICON); + pointerIcon = skin.getDrawable(POINTER_ICON); setTouchable(Touchable.disabled); resize(); @@ -56,6 +56,10 @@ private void getInputUnproject(Viewport v, Vector2 out) { @Override public void act(float delta) { + + if (pointerIcon instanceof AnimationDrawable) + ((AnimationDrawable) pointerIcon).act(delta); + super.act(delta); if (getStage().getActors().get(getStage().getActors().size - 1) != this) @@ -69,12 +73,12 @@ public void draw(Batch batch, float alpha) { setPosition(mousepos.x - getWidth() / 2, mousepos.y - getHeight() / 2); - batch.draw(pointerIcon, getX(), getY(), getWidth(), getHeight()); + pointerIcon.draw(batch, getX(), getY(), getWidth(), getHeight()); } public void resize() { - pointerScale = DPIUtils.getTouchMinSize() / pointerIcon.getRegionHeight() * .8f; - setSize(pointerIcon.getRegionWidth() * pointerScale, pointerIcon.getRegionHeight() * pointerScale); + pointerScale = DPIUtils.getTouchMinSize() / pointerIcon.getMinHeight() * .8f; + setSize(pointerIcon.getMinWidth() * pointerScale, pointerIcon.getMinHeight() * pointerScale); } public void show() { diff --git a/blade-engine/src/com/bladecoder/engine/ui/defaults/DefaultSceneScreen.java b/blade-engine/src/com/bladecoder/engine/ui/defaults/DefaultSceneScreen.java index 4445b6863..bf2131e7d 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/defaults/DefaultSceneScreen.java +++ b/blade-engine/src/com/bladecoder/engine/ui/defaults/DefaultSceneScreen.java @@ -29,7 +29,6 @@ import com.badlogic.gdx.graphics.g2d.GlyphLayout; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.TextureAtlas; -import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.input.GestureDetector; import com.badlogic.gdx.math.Interpolation; @@ -62,6 +61,7 @@ import com.bladecoder.engine.model.World; import com.bladecoder.engine.model.World.AssetState; import com.bladecoder.engine.model.WorldListener; +import com.bladecoder.engine.ui.AnimationDrawable; import com.bladecoder.engine.ui.DialogUI; import com.bladecoder.engine.ui.ITextManagerUI; import com.bladecoder.engine.ui.InventoryButton; @@ -253,8 +253,7 @@ void updateUI() { /** * Sets the game speed. Can be used to fastfordward * - * @param s - * The multiplier speed. ej. 2.0 + * @param s The multiplier speed. ej. 2.0 */ @Override public void setSpeed(float s) { @@ -304,6 +303,18 @@ private void update(float delta) { } stage.act(delta); + pointer.update(delta); + + if (drawHotspots) { + Drawable hotspotDrawable = getUI().getSkin().getDrawable(Verb.LEAVE_VERB); + Drawable leaveDrawable = getUI().getSkin().getDrawable("hotspot"); + + if (hotspotDrawable != null && hotspotDrawable instanceof AnimationDrawable) + ((AnimationDrawable) hotspotDrawable).act(delta); + + if (leaveDrawable != null && leaveDrawable instanceof AnimationDrawable) + ((AnimationDrawable) leaveDrawable).act(delta); + } if (world.isPaused()) return; @@ -332,11 +343,11 @@ private void update(float delta) { Verb leaveVerb = currentActor.getVerb(Verb.LEAVE_VERB); - TextureRegion r = null; + Drawable r = null; if (leaveVerb != null) { if (leaveVerb.getIcon() != null - && (r = getUI().getSkin().getAtlas().findRegion(leaveVerb.getIcon())) != null) { + && (r = getUI().getSkin().getDrawable(leaveVerb.getIcon())) != null) { pointer.setIcon(r); } else { @@ -346,7 +357,7 @@ private void update(float delta) { Verb actionVerb = currentActor.getVerb(Verb.ACTION_VERB); if (actionVerb != null && actionVerb.getIcon() != null - && (r = getUI().getSkin().getAtlas().findRegion(actionVerb.getIcon())) != null) { + && (r = getUI().getSkin().getDrawable(actionVerb.getIcon())) != null) { pointer.setIcon(r); } else { pointer.setHotspotIcon(); diff --git a/blade-engine/src/com/bladecoder/engine/ui/defaults/ScenePointer.java b/blade-engine/src/com/bladecoder/engine/ui/defaults/ScenePointer.java index ee677d277..0db96d567 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/defaults/ScenePointer.java +++ b/blade-engine/src/com/bladecoder/engine/ui/defaults/ScenePointer.java @@ -21,13 +21,15 @@ import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.GlyphLayout; import com.badlogic.gdx.graphics.g2d.SpriteBatch; -import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.scenes.scene2d.ui.Skin; +import com.badlogic.gdx.scenes.scene2d.utils.Drawable; +import com.badlogic.gdx.scenes.scene2d.utils.TransformDrawable; import com.badlogic.gdx.utils.viewport.Viewport; import com.bladecoder.engine.i18n.I18N; import com.bladecoder.engine.model.ActorRenderer; import com.bladecoder.engine.model.SpriteActor; +import com.bladecoder.engine.ui.AnimationDrawable; import com.bladecoder.engine.util.DPIUtils; import com.bladecoder.engine.util.RectangleRenderer; @@ -44,10 +46,10 @@ public class ScenePointer { private String desc = null; - private TextureRegion leaveIcon; - private TextureRegion pointerIcon; - private TextureRegion hotspotIcon; - private TextureRegion currentIcon; + private Drawable leaveIcon; + private Drawable pointerIcon; + private Drawable hotspotIcon; + private Drawable currentIcon; private SpriteActor draggingActor; private final Vector2 mousepos = new Vector2(); @@ -61,9 +63,9 @@ public class ScenePointer { public ScenePointer(Skin skin) { // this.skin = skin; font = skin.getFont("desc"); - pointerIcon = skin.getAtlas().findRegion(POINTER_ICON); - leaveIcon = skin.getAtlas().findRegion(LEAVE_ICON); - hotspotIcon = skin.getAtlas().findRegion(HOTSPOT_ICON); + pointerIcon = skin.getDrawable(POINTER_ICON); + leaveIcon = skin.getDrawable(LEAVE_ICON); + hotspotIcon = skin.getDrawable(HOTSPOT_ICON); reset(); } @@ -94,7 +96,7 @@ public void setHotspotIcon() { currentIcon = hotspotIcon; } - public void setIcon(TextureRegion r) { + public void setIcon(Drawable r) { currentIcon = r; } @@ -116,6 +118,11 @@ private void getInputUnproject(Viewport v, Vector2 out) { v.unproject(out); } + public void update(float delta) { + if (pointerIcon instanceof AnimationDrawable) + ((AnimationDrawable) pointerIcon).act(delta); + } + public void draw(SpriteBatch batch, Viewport v) { getInputUnproject(v, mousepos); @@ -142,10 +149,22 @@ public void draw(SpriteBatch batch, Viewport v) { if (draggingActor == null) { if (!multiTouch || (currentIcon == leaveIcon && Gdx.input.isTouched())) { - batch.draw(currentIcon, mousepos.x - currentIcon.getRegionWidth() / 2, - mousepos.y - currentIcon.getRegionHeight() / 2, currentIcon.getRegionWidth() / 2, - currentIcon.getRegionHeight() / 2, currentIcon.getRegionWidth(), currentIcon.getRegionHeight(), - pointerScale, pointerScale, currentIcon == leaveIcon ? leaveRotation : 0); + if (currentIcon instanceof TransformDrawable) { + + TransformDrawable i = (TransformDrawable) currentIcon; + + i.draw(batch, mousepos.x - currentIcon.getMinWidth() / 2, + mousepos.y - currentIcon.getMinHeight() / 2, currentIcon.getMinWidth() / 2, + currentIcon.getMinHeight() / 2, currentIcon.getMinWidth(), currentIcon.getMinHeight(), + pointerScale, pointerScale, currentIcon == leaveIcon ? leaveRotation : 0); + } else { + float width = currentIcon.getMinWidth() * pointerScale; + float height = currentIcon.getMinHeight() * pointerScale; + float x = mousepos.x - width / 2; + float y = mousepos.y - height / 2; + + currentIcon.draw(batch, x, y, width, height); + } } } else { float h = (draggingActor.getHeight() > draggingActor.getWidth() ? draggingActor.getHeight() @@ -160,7 +179,7 @@ public void draw(SpriteBatch batch, Viewport v) { } public void resize(int width, int height) { - pointerScale = DPIUtils.getTouchMinSize() / pointerIcon.getRegionHeight() * .8f; + pointerScale = DPIUtils.getTouchMinSize() / pointerIcon.getMinHeight() * .8f; } } From f84f0bce2265d8c3996114250da7a83965308615 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Mon, 8 Jul 2019 10:26:25 +0200 Subject: [PATCH 018/147] Update RoboVM to v2.3.7 --- gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index dada79746..b5321d8d4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ version=3.1.2-SNAPSHOT libgdxVersion=1.9.9 -roboVMVersion=2.3.5 -roboVMGradlePluginVersion=2.3.5 +roboVMVersion=2.3.7 +roboVMGradlePluginVersion=2.3.7 buildToolsVersion=28.0.3 androidAPILevel=27 androidGradlePluginVersion=3.2.1 From ff5d9fcd62e34ea0c0e2ed714ee8d2ddecc0dc20 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Tue, 9 Jul 2019 15:08:32 +0200 Subject: [PATCH 019/147] Animation button fixes. --- .../src/main/resources/versions.properties | 4 +-- .../com/bladecoder/engine/ui/BladeSkin.java | 10 ++++-- .../bladecoder/engine/ui/InventoryButton.java | 36 +++++++++---------- .../com/bladecoder/engine/ui/MenuScreen.java | 6 ++-- .../ui/defaults/DefaultSceneScreen.java | 21 ++++++++--- 5 files changed, 45 insertions(+), 32 deletions(-) diff --git a/adventure-editor/src/main/resources/versions.properties b/adventure-editor/src/main/resources/versions.properties index f7c61fbe7..4e85e6d46 100644 --- a/adventure-editor/src/main/resources/versions.properties +++ b/adventure-editor/src/main/resources/versions.properties @@ -3,6 +3,6 @@ androidGradlePluginVersion=3.2.1 bladeInkVersion=0.5.1 buildToolsVersion=28.0.3 libgdxVersion=1.9.9 -roboVMGradlePluginVersion=2.3.5 -roboVMVersion=2.3.5 +roboVMGradlePluginVersion=2.3.7 +roboVMVersion=2.3.7 version=3.1.2-SNAPSHOT diff --git a/blade-engine/src/com/bladecoder/engine/ui/BladeSkin.java b/blade-engine/src/com/bladecoder/engine/ui/BladeSkin.java index 465f4be47..4f174ad78 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/BladeSkin.java +++ b/blade-engine/src/com/bladecoder/engine/ui/BladeSkin.java @@ -175,16 +175,20 @@ else if (size != -1) // TODO set size in points (dpi @Override public AnimationDrawable read(Json json, JsonValue jsonData, @SuppressWarnings("rawtypes") Class type) { String name = json.readValue("name", String.class, jsonData); - float frameDuration = json.readValue("frame_duration", Float.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); - Animation a = new Animation(frameDuration, regions, PlayMode.LOOP); + 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 + ", " + frameDuration + ")"); + named.setName(jsonData.name + " (" + name + ", " + duration + ")"); } return drawable; diff --git a/blade-engine/src/com/bladecoder/engine/ui/InventoryButton.java b/blade-engine/src/com/bladecoder/engine/ui/InventoryButton.java index ce609b0e8..97924c3b4 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/InventoryButton.java +++ b/blade-engine/src/com/bladecoder/engine/ui/InventoryButton.java @@ -1,70 +1,68 @@ package com.bladecoder.engine.ui; - import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.actions.Actions; -import com.badlogic.gdx.scenes.scene2d.ui.Button; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.bladecoder.engine.ui.InventoryUI.InventoryPos; import com.bladecoder.engine.util.DPIUtils; -public class InventoryButton extends Button { - +public class InventoryButton extends AnimButton { + private final InventoryUI inventory; private final UI ui; - + private int numItems = Integer.MAX_VALUE; public InventoryButton(UI ui, InventoryUI inv) { super(ui.getSkin(), "inventory"); this.inventory = inv; this.ui = ui; - + addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { - if(!inventory.isVisible()) + if (!inventory.isVisible()) inventory.show(); else inventory.hide(); } }); } - + @Override public void act(float delta) { super.act(delta); - - if(numItems < ui.getWorld().getInventory().getNumItems()) { + + if (numItems < ui.getWorld().getInventory().getNumItems()) { // addAction(Actions.repeat(30, Actions.sequence(Actions.rotateBy(40, 1f), Actions.rotateBy(-40, 1f)))); addAction(Actions.repeat(4, Actions.sequence(Actions.moveBy(20, 0, .06f), Actions.moveBy(-20, 0, .06f)))); // addAction(Actions.repeat(3, Actions.sequence(Actions.alpha(0, .4f), Actions.alpha(1, .4f)))); } - + numItems = ui.getWorld().getInventory().getNumItems(); } - + public void resize(int width, int height) { float size = DPIUtils.getPrefButtonSize(); float margin = DPIUtils.getMarginSize(); - + setSize(size, size); - + InventoryPos inventoryPos = inventory.getInventoryPos(); - + switch (inventoryPos) { case TOP: - setPosition(margin, height - margin - getHeight()); + setPosition(margin, height - margin - getHeight()); break; case LEFT: - setPosition(margin, margin); + setPosition(margin, margin); break; case RIGHT: - setPosition(width - margin - getWidth(), margin); + setPosition(width - margin - getWidth(), margin); break; case DOWN: case CENTER: - setPosition(margin, margin); + setPosition(margin, margin); break; default: break; diff --git a/blade-engine/src/com/bladecoder/engine/ui/MenuScreen.java b/blade-engine/src/com/bladecoder/engine/ui/MenuScreen.java index c0057ae0e..dff1da2d4 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/MenuScreen.java +++ b/blade-engine/src/com/bladecoder/engine/ui/MenuScreen.java @@ -292,7 +292,7 @@ public void clicked(InputEvent event, float x, float y) { stage.addActor(menuButtonTable); // BOTTOM-RIGHT BUTTON STACK - credits = new Button(skin, "credits"); + credits = new AnimButton(skin, "credits"); credits.addListener(new ClickListener() { @Override public void clicked(InputEvent event, float x, float y) { @@ -300,7 +300,7 @@ public void clicked(InputEvent event, float x, float y) { } }); - help = new Button(skin, "help"); + help = new AnimButton(skin, "help"); help.addListener(new ClickListener() { @Override public void clicked(InputEvent event, float x, float y) { @@ -308,7 +308,7 @@ public void clicked(InputEvent event, float x, float y) { } }); - debug = new Button(skin, "debug"); + debug = new AnimButton(skin, "debug"); debug.addListener(new ClickListener() { @Override public void clicked(InputEvent event, float x, float y) { diff --git a/blade-engine/src/com/bladecoder/engine/ui/defaults/DefaultSceneScreen.java b/blade-engine/src/com/bladecoder/engine/ui/defaults/DefaultSceneScreen.java index bf2131e7d..4e38cd97d 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/defaults/DefaultSceneScreen.java +++ b/blade-engine/src/com/bladecoder/engine/ui/defaults/DefaultSceneScreen.java @@ -46,6 +46,7 @@ import com.badlogic.gdx.scenes.scene2d.utils.Drawable; import com.badlogic.gdx.scenes.scene2d.utils.TransformDrawable; import com.badlogic.gdx.utils.Align; +import com.badlogic.gdx.utils.GdxRuntimeException; import com.badlogic.gdx.utils.viewport.Viewport; import com.bladecoder.engine.assets.EngineAssetManager; import com.bladecoder.engine.i18n.I18N; @@ -61,6 +62,7 @@ import com.bladecoder.engine.model.World; import com.bladecoder.engine.model.World.AssetState; import com.bladecoder.engine.model.WorldListener; +import com.bladecoder.engine.ui.AnimButton; import com.bladecoder.engine.ui.AnimationDrawable; import com.bladecoder.engine.ui.DialogUI; import com.bladecoder.engine.ui.ITextManagerUI; @@ -346,8 +348,7 @@ private void update(float delta) { Drawable r = null; if (leaveVerb != null) { - if (leaveVerb.getIcon() != null - && (r = getUI().getSkin().getDrawable(leaveVerb.getIcon())) != null) { + if ((r = getDrawable(leaveVerb.getIcon())) != null) { pointer.setIcon(r); } else { @@ -356,8 +357,7 @@ private void update(float delta) { } else { Verb actionVerb = currentActor.getVerb(Verb.ACTION_VERB); - if (actionVerb != null && actionVerb.getIcon() != null - && (r = getUI().getSkin().getDrawable(actionVerb.getIcon())) != null) { + if (actionVerb != null && (r = getDrawable(actionVerb.getIcon())) != null) { pointer.setIcon(r); } else { pointer.setHotspotIcon(); @@ -372,6 +372,17 @@ private void update(float delta) { } } + private Drawable getDrawable(String name) { + if (name == null) + return null; + + try { + return getUI().getSkin().getDrawable(name); + } catch (GdxRuntimeException e) { + return null; + } + } + private InteractiveActor getActorUnderCursor() { final float tolerance; @@ -872,7 +883,7 @@ public void setUI(final UI ui) { pie = new PieMenu(this); textManagerUI = new TextManagerUI(ui); - menuButton = new Button(ui.getSkin(), "menu"); + menuButton = new AnimButton(ui.getSkin(), "menu"); dialogUI = new DialogUI(ui); pointer = new ScenePointer(ui.getSkin()); inventoryUI = new InventoryUI(this, pointer); From 1fc686bfb7420875afe1a4af2984f35037816b68 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Wed, 10 Jul 2019 09:04:26 +0200 Subject: [PATCH 020/147] Fix bug in anim cursors. --- .../src/com/bladecoder/engine/ui/defaults/ScenePointer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blade-engine/src/com/bladecoder/engine/ui/defaults/ScenePointer.java b/blade-engine/src/com/bladecoder/engine/ui/defaults/ScenePointer.java index 0db96d567..91dd286d9 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/defaults/ScenePointer.java +++ b/blade-engine/src/com/bladecoder/engine/ui/defaults/ScenePointer.java @@ -119,8 +119,8 @@ private void getInputUnproject(Viewport v, Vector2 out) { } public void update(float delta) { - if (pointerIcon instanceof AnimationDrawable) - ((AnimationDrawable) pointerIcon).act(delta); + if (currentIcon instanceof AnimationDrawable) + ((AnimationDrawable) currentIcon).act(delta); } public void draw(SpriteBatch batch, Viewport v) { From aff8484ac07b2f599ab3347f4a13f36005f1e487 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Wed, 10 Jul 2019 10:25:31 +0200 Subject: [PATCH 021/147] Strip whithespace optional when creating atlases. --- CHANGELOG.md | 8 ++++++++ .../com/bladecoder/engineeditor/common/ImageUtils.java | 9 +++++---- .../bladecoder/engineeditor/scneditor/ToolsWindow.java | 9 +-------- .../bladecoder/engineeditor/ui/CreateAtlasDialog.java | 2 +- adventure-editor/src/main/resources/versions.properties | 2 +- gradle.properties | 2 +- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af29a1b85..caf348639 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [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. 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/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/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/resources/versions.properties b/adventure-editor/src/main/resources/versions.properties index 4e85e6d46..5de734792 100644 --- a/adventure-editor/src/main/resources/versions.properties +++ b/adventure-editor/src/main/resources/versions.properties @@ -5,4 +5,4 @@ buildToolsVersion=28.0.3 libgdxVersion=1.9.9 roboVMGradlePluginVersion=2.3.7 roboVMVersion=2.3.7 -version=3.1.2-SNAPSHOT +version=3.1.2 diff --git a/gradle.properties b/gradle.properties index b5321d8d4..d7e91d962 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=3.1.2-SNAPSHOT +version=3.1.2 libgdxVersion=1.9.9 roboVMVersion=2.3.7 roboVMGradlePluginVersion=2.3.7 From d9850007e7e278f7f1bff238ece9d9debbdc12e0 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Wed, 10 Jul 2019 14:57:49 +0200 Subject: [PATCH 022/147] Updated Blade Ink to v0.7.0 --- gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index d7e91d962..550a7fd91 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,8 @@ -version=3.1.2 +version=3.1.3-SNAPSHOT libgdxVersion=1.9.9 roboVMVersion=2.3.7 roboVMGradlePluginVersion=2.3.7 buildToolsVersion=28.0.3 androidAPILevel=27 androidGradlePluginVersion=3.2.1 -bladeInkVersion=0.5.1 +bladeInkVersion=0.7.0 From dc29cf1cdc5d54bdff1026c854f0c5840c54bd79 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Thu, 11 Jul 2019 23:54:19 +0200 Subject: [PATCH 023/147] Set Snapshot version. --- adventure-editor/src/main/resources/versions.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adventure-editor/src/main/resources/versions.properties b/adventure-editor/src/main/resources/versions.properties index 5de734792..85e9891de 100644 --- a/adventure-editor/src/main/resources/versions.properties +++ b/adventure-editor/src/main/resources/versions.properties @@ -1,8 +1,8 @@ androidAPILevel=27 androidGradlePluginVersion=3.2.1 -bladeInkVersion=0.5.1 +bladeInkVersion=0.7.0 buildToolsVersion=28.0.3 libgdxVersion=1.9.9 roboVMGradlePluginVersion=2.3.7 roboVMVersion=2.3.7 -version=3.1.2 +version=3.1.3-SNAPSHOT From b02bdcfba9a90037acd0d6fb2c080f13622ccd06 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Wed, 17 Jul 2019 14:42:30 +0200 Subject: [PATCH 024/147] Use tags in .json file to reduce size. --- .../engineeditor/common/ActionDetector.java | 2 +- .../engine/actions/ActionFactory.java | 90 +++++++++++++++++-- .../com/bladecoder/engine/ink/InkManager.java | 7 +- .../engine/serialization/BladeJson.java | 44 +++++++++ .../serialization/WorldSerialization.java | 14 ++- .../bladecoder/engine/util/ActionUtils.java | 8 +- 6 files changed, 141 insertions(+), 24 deletions(-) diff --git a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/ActionDetector.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/ActionDetector.java index 1b9de37ed..651c7742b 100644 --- a/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/ActionDetector.java +++ b/adventure-editor/src/main/java/com/bladecoder/engineeditor/common/ActionDetector.java @@ -106,7 +106,7 @@ public static Action create(String name, HashMap 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/blade-engine/src/com/bladecoder/engine/actions/ActionFactory.java b/blade-engine/src/com/bladecoder/engine/actions/ActionFactory.java index ea9531a9e..e4ea3d576 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,21 +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(SetActorAttrAction.class); + addClassTag(SetCutmodeAction.class); + addClassTag(SetDialogOptionAttrAction.class); + addClassTag(SetPlayerAction.class); + addClassTag(SetSceneStateAction.class); + addClassTag(SetStateAction.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); - a = (Action) ClassReflection.newInstance(c); + Class c = tagToClass.get(tag); + + if (c != null) { + a = (Action) ClassReflection.newInstance(c); + } else { + c = Class.forName(tag, true, loader); + a = (Action) ClassReflection.newInstance(c); + } if (params != null) { @@ -48,9 +123,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/ink/InkManager.java b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java index f08996b17..fa82ef6f6 100644 --- a/blade-engine/src/com/bladecoder/engine/ink/InkManager.java +++ b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java @@ -303,8 +303,7 @@ private void processCommand(HashMap params, String line) { Action action; try { - action = ActionFactory.createByClass("com.bladecoder.engine.actions." + commandName + "Action", - params); + action = ActionFactory.create(commandName, params); action.init(w); actions.add(action); } catch (ClassNotFoundException | ReflectionException e) { @@ -348,9 +347,9 @@ 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); diff --git a/blade-engine/src/com/bladecoder/engine/serialization/BladeJson.java b/blade-engine/src/com/bladecoder/engine/serialization/BladeJson.java index 04cf390d8..5c22c917f 100644 --- a/blade-engine/src/com/bladecoder/engine/serialization/BladeJson.java +++ b/blade-engine/src/com/bladecoder/engine/serialization/BladeJson.java @@ -1,6 +1,22 @@ 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.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; /** @@ -24,6 +40,30 @@ public BladeJson(World w, Mode mode, boolean init) { 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 +85,8 @@ public boolean getInit() { public void setInit(boolean init) { this.init = init; } + + 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 9f94237ab..f5f7b5b42 100644 --- a/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java +++ b/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java @@ -126,12 +126,10 @@ public void loadChapter() throws IOException { /** * Loads a JSON chapter file. * - * @param chapterName - * filename without path and extension. - * @param scene - * the init scene. null to use the chapter defined init scene. - * @param initScene - * false only when it comes from loading a saved game. + * @param chapterName filename without path and extension. + * @param scene the init scene. null to use the chapter defined init + * scene. + * @param initScene false only when it comes from loading a saved game. * @throws IOException */ public void loadChapter(String chapterName, String scene, boolean initScene) throws IOException { @@ -462,7 +460,7 @@ private void cacheSounds() { 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) { @@ -514,7 +512,7 @@ private void cacheSounds() { params.put("sound", sd.getId()); try { - Action a2 = ActionFactory.createByClass(PlaySoundAction.class.getName(), + Action a2 = ActionFactory.create(PlaySoundAction.class.getName(), params); actions.set(i, a2); a2.init(w); diff --git a/blade-engine/src/com/bladecoder/engine/util/ActionUtils.java b/blade-engine/src/com/bladecoder/engine/util/ActionUtils.java index b50fde26e..32e78be75 100644 --- a/blade-engine/src/com/bladecoder/engine/util/ActionUtils.java +++ b/blade-engine/src/com/bladecoder/engine/util/ActionUtils.java @@ -26,7 +26,7 @@ public class ActionUtils { public static String getInfo(Class clazz) { return clazz.getAnnotation(ActionDescription.class).value(); } - + public static boolean isDeprecated(Class clazz) { return clazz.getAnnotation(Deprecated.class) != null; } @@ -342,13 +342,13 @@ public static void writeJson(Action a, Json json) { public static Action readJson(World w, Json json, JsonValue jsonData) { String className = jsonData.getString("class", null); + Action action = null; if (className != null) { jsonData.remove("class"); try { - action = ActionFactory.createByClass(className, null); - + action = ActionFactory.create(className, null); } catch (ClassNotFoundException | ReflectionException e1) { throw new SerializationException(e1); } @@ -367,7 +367,7 @@ public static Action readJson(World w, Json json, JsonValue jsonData) { + (v == null ? "null" : v.asString())); } } - + action.init(w); } From 081a1dd256f933ae98cd67c431a8366c0230ac99 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Fri, 19 Jul 2019 14:53:36 +0200 Subject: [PATCH 025/147] Updated libgdx to v1.9.10 --- .../ui/panels/FilteredSelectBox.java | 1122 +++++++++-------- .../src/main/resources/versions.properties | 6 +- .../engine/actions/ActionFactory.java | 7 +- .../serialization/WorldSerialization.java | 6 +- gradle.properties | 6 +- 5 files changed, 613 insertions(+), 534 deletions(-) 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/resources/versions.properties b/adventure-editor/src/main/resources/versions.properties index 85e9891de..b2cf357f1 100644 --- a/adventure-editor/src/main/resources/versions.properties +++ b/adventure-editor/src/main/resources/versions.properties @@ -1,8 +1,8 @@ -androidAPILevel=27 -androidGradlePluginVersion=3.2.1 +androidAPILevel=28 +androidGradlePluginVersion=3.4.1 bladeInkVersion=0.7.0 buildToolsVersion=28.0.3 -libgdxVersion=1.9.9 +libgdxVersion=1.9.10 roboVMGradlePluginVersion=2.3.7 roboVMVersion=2.3.7 version=3.1.3-SNAPSHOT diff --git a/blade-engine/src/com/bladecoder/engine/actions/ActionFactory.java b/blade-engine/src/com/bladecoder/engine/actions/ActionFactory.java index e4ea3d576..cbd06d410 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/ActionFactory.java +++ b/blade-engine/src/com/bladecoder/engine/actions/ActionFactory.java @@ -109,13 +109,12 @@ public static Action create(String tag, HashMap params) Class c = tagToClass.get(tag); - if (c != null) { - a = (Action) ClassReflection.newInstance(c); - } else { + if (c == null) { c = Class.forName(tag, true, loader); - a = (Action) ClassReflection.newInstance(c); } + a = (Action) ClassReflection.newInstance(c); + if (params != null) { for (String key : params.keySet()) { diff --git a/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java b/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java index f5f7b5b42..537a79afb 100644 --- a/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java +++ b/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java @@ -181,8 +181,7 @@ public void saveModel(String chapterId) throws IOException { Json json = new BladeJson(w, Mode.MODEL); json.setOutputType(OutputType.javascript); - // TODO: New libgdx field. Waiting for release! - // json.setSortFields(true); + json.setSortFields(true); String s = null; @@ -512,8 +511,7 @@ private void cacheSounds() { params.put("sound", sd.getId()); try { - Action a2 = ActionFactory.create(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/gradle.properties b/gradle.properties index 550a7fd91..0d8e21b2c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,8 @@ version=3.1.3-SNAPSHOT -libgdxVersion=1.9.9 +libgdxVersion=1.9.10 roboVMVersion=2.3.7 roboVMGradlePluginVersion=2.3.7 buildToolsVersion=28.0.3 -androidAPILevel=27 -androidGradlePluginVersion=3.2.1 +androidAPILevel=28 +androidGradlePluginVersion=3.4.1 bladeInkVersion=0.7.0 From 347461e24790a0342046c6ae95ee604914614903 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Fri, 19 Jul 2019 15:31:53 +0200 Subject: [PATCH 026/147] Updated gradle. --- .../projectTmpl/gradle/wrapper/gradle-wrapper.properties | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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..558870dad 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-5.1.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e0b3fb8d7..558870dad 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/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-5.1.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 822e459ffe12cd22f9b34d1045e73bf1316a4284 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Mon, 22 Jul 2019 10:37:01 +0200 Subject: [PATCH 027/147] Update gradle to v5.4.1 --- .../projectTmpl/android/build.gradle | 57 +------------------ .../resources/projectTmpl/core/build.gradle | 1 - .../projectTmpl/desktop/build.gradle | 1 - .../gradle/wrapper/gradle-wrapper.properties | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 5 files changed, 3 insertions(+), 60 deletions(-) diff --git a/adventure-editor/src/main/resources/projectTmpl/android/build.gradle b/adventure-editor/src/main/resources/projectTmpl/android/build.gradle index e74a5fea7..8a0397ce1 100644 --- a/adventure-editor/src/main/resources/projectTmpl/android/build.gradle +++ b/adventure-editor/src/main/resources/projectTmpl/android/build.gradle @@ -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" @@ -175,61 +175,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/core/build.gradle b/adventure-editor/src/main/resources/projectTmpl/core/build.gradle index aac4e67c4..697bc58f3 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" diff --git a/adventure-editor/src/main/resources/projectTmpl/desktop/build.gradle b/adventure-editor/src/main/resources/projectTmpl/desktop/build.gradle index 355ddeb35..549d9eeda 100644 --- a/adventure-editor/src/main/resources/projectTmpl/desktop/build.gradle +++ b/adventure-editor/src/main/resources/projectTmpl/desktop/build.gradle @@ -7,7 +7,6 @@ targetCompatibility=1.7 sourceSets.main.resources.srcDirs += [ rootProject.file('assets').absolutePath ] mainClassName = "%PACKAGE%.desktop.DesktopLauncher" -eclipse.project.name = appName + '-desktop' dependencies { implementation project(":core") 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 558870dad..f4d7b2bf6 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-5.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 558870dad..f4d7b2bf6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 71142a9516f98b32309a552ee8f9bdd01eaed0df Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Tue, 30 Jul 2019 19:11:08 +0200 Subject: [PATCH 028/147] Fix to detect character actors in use verbs. --- .../src/com/bladecoder/engine/ui/InventoryUI.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/blade-engine/src/com/bladecoder/engine/ui/InventoryUI.java b/blade-engine/src/com/bladecoder/engine/ui/InventoryUI.java index e4496857e..3d2bbc18e 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/InventoryUI.java +++ b/blade-engine/src/com/bladecoder/engine/ui/InventoryUI.java @@ -233,6 +233,10 @@ public void resize(int width, int height) { } } + public void setStyle(String name) { + style = sceneScreen.getUI().getSkin().get(name, InventoryUIStyle.class); + } + public void retrieveAssets(TextureAtlas atlas) { } @@ -268,8 +272,8 @@ public void draw(Batch batch, float alpha) { } r.draw((SpriteBatch) batch, getX() + x * tileSize + x * rowSpace + tileSize / 2 + margin, - getY() + (tileSize - r.getHeight() * size) / 2 + y * tileSize + y * rowSpace + margin, size, size, 0f, - a.getTint()); + getY() + (tileSize - r.getHeight() * size) / 2 + y * tileSize + y * rowSpace + margin, size, size, + 0f, a.getTint()); } super.draw(batch, alpha); @@ -325,10 +329,10 @@ private void use(InteractiveActor targetActor, InteractiveActor invActor) { bestMatch = vinv; } - if (vtarget == bestMatch) { - sceneScreen.runVerb(targetActor, "use", invActor.getId()); - } else { + if (vinv == bestMatch) { sceneScreen.runVerb(invActor, "use", targetActor.getId()); + } else { + sceneScreen.runVerb(targetActor, "use", invActor.getId()); } } From 82434280eda013ed0d04fe55511a17a7c6f4d8d4 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Fri, 2 Aug 2019 19:20:16 +0200 Subject: [PATCH 029/147] Fix travis. --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index d6e5d91c5..014449154 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,3 @@ language: Java -jdk: - - oraclejdk8 From 18009253be1eeacabf839ec990860e6f37cd0435 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Fri, 16 Aug 2019 14:47:51 +0200 Subject: [PATCH 030/147] Updated Spine to v3.8. --- blade-engine-spine-plugin/build.gradle | 2 +- .../java/com/bladecoder/engine/spine/SpineRenderer.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/blade-engine-spine-plugin/build.gradle b/blade-engine-spine-plugin/build.gradle index 415323fa1..33121732f 100644 --- a/blade-engine-spine-plugin/build.gradle +++ b/blade-engine-spine-plugin/build.gradle @@ -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") } 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 231c72f55..fd4cbedad 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,11 +309,11 @@ 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 { From 7f2031491a239e4f23d47a90ad114dc0a8293496 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sat, 17 Aug 2019 19:49:33 +0200 Subject: [PATCH 031/147] Fix dialog info label overun. --- .../engineeditor/ui/panels/EditDialog.java | 30 ++++++++++--------- .../engine/actions/IfPropertyAction.java | 14 ++++----- 2 files changed, 23 insertions(+), 21 deletions(-) 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/blade-engine/src/com/bladecoder/engine/actions/IfPropertyAction.java b/blade-engine/src/com/bladecoder/engine/actions/IfPropertyAction.java index 56516ff05..26d10c0ef 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,12 @@ public void init(World w) { @Override public boolean run(VerbRunner cb) { String valDest = w.getCustomProperty(name); - - if(valDest == null) + + if (valDest == null) valDest = Config.getProperty(name, null); - + if (!ActionUtils.compareNullStr(value, valDest)) { - gotoElse((VerbRunner) cb); + gotoElse(cb); } return false; From aa863bb6ef373034b9af79c0bd7cff6783225dbe Mon Sep 17 00:00:00 2001 From: rgarcia Date: Fri, 23 Aug 2019 10:10:22 +0200 Subject: [PATCH 032/147] Added INSIDE property to IfAttrProperty action. --- .../bladecoder/engine/actions/IfAttrAction.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/blade-engine/src/com/bladecoder/engine/actions/IfAttrAction.java b/blade-engine/src/com/bladecoder/engine/actions/IfAttrAction.java index 7e962ac21..60270e85e 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/IfAttrAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/IfAttrAction.java @@ -30,7 +30,7 @@ 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) @@ -136,6 +136,18 @@ public boolean run(VerbRunner cb) { gotoElse(cb); } } + } else if (attr.equals(ActorAttribute.INSIDE)) { + BaseActor insideActor = w.getCurrentScene().getActor(value, false); + boolean inside = false; + + if (a != null && insideActor != null) + inside = insideActor.getBBox().contains(a.getX(), a.getY()); + else + EngineLogger.debug("Actor for inside test not found: " + value); + + if (!inside) { + gotoElse(cb); + } } return false; From 983462219ea6232302dcaaac1b4710ec4c61d51f Mon Sep 17 00:00:00 2001 From: rgarcia Date: Tue, 27 Aug 2019 09:57:26 +0200 Subject: [PATCH 033/147] Added 'show_hotspots' config key to enable/disable the show hotspots feature. --- .../engineeditor/ui/WorldProps.java | 19 ++++++---- .../ui/defaults/DefaultSceneScreen.java | 11 +++++- .../com/bladecoder/engine/util/Config.java | 35 ++++++++++--------- 3 files changed, 41 insertions(+), 24 deletions(-) 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..da2ec9867 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, @@ -96,6 +101,8 @@ private void setProject() { Ctx.project.getProjectConfig().getProperty(Config.CHARACTER_ICON_ATLAS, "")); addProperty(Config.EXTEND_VIEWPORT_PROP, Boolean .parseBoolean(Ctx.project.getProjectConfig().getProperty(Config.EXTEND_VIEWPORT_PROP, "true"))); + addProperty(Config.SHOW_HOTSPOTS, + Boolean.parseBoolean(Ctx.project.getProjectConfig().getProperty(Config.SHOW_HOTSPOTS, "true"))); } invalidateHierarchy(); diff --git a/blade-engine/src/com/bladecoder/engine/ui/defaults/DefaultSceneScreen.java b/blade-engine/src/com/bladecoder/engine/ui/defaults/DefaultSceneScreen.java index 4e38cd97d..379efb5f0 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/defaults/DefaultSceneScreen.java +++ b/blade-engine/src/com/bladecoder/engine/ui/defaults/DefaultSceneScreen.java @@ -117,7 +117,11 @@ public static enum UIModes { // Actor under the cursor private InteractiveActor currentActor = null; + // Called by the input handlers to show/hide the hotspots. private boolean drawHotspots = false; + // Configuration to enable/disable the 'drawHotspot' feature. + private boolean showHotspotsFeature = true; + private final boolean showDesc; private final boolean fastLeave; @@ -138,6 +142,7 @@ public DefaultSceneScreen() { : new SceneFitViewport(); showDesc = Config.getProperty(Config.SHOW_DESC_PROP, true); fastLeave = Config.getProperty(Config.FAST_LEAVE, false); + showHotspotsFeature = Config.getProperty(Config.SHOW_HOTSPOTS, true); } @Override @@ -162,7 +167,11 @@ public boolean getDrawHotspots() { } public void setDrawHotspots(boolean drawHotspots) { - this.drawHotspots = drawHotspots; + this.drawHotspots = drawHotspots && showHotspotsFeature; + } + + public void setShowHotspotsFeature(boolean v) { + showHotspotsFeature = v; } public UIModes getUIMode() { diff --git a/blade-engine/src/com/bladecoder/engine/util/Config.java b/blade-engine/src/com/bladecoder/engine/util/Config.java index cfc53f04d..5923ababd 100644 --- a/blade-engine/src/com/bladecoder/engine/util/Config.java +++ b/blade-engine/src/com/bladecoder/engine/util/Config.java @@ -20,13 +20,13 @@ import com.bladecoder.engine.assets.EngineAssetManager; public class Config { - public static final String INVENTORY_POS_PROP="inventory_pos"; - public static final String INVENTORY_AUTOSIZE_PROP="inventory_autosize"; + public static final String INVENTORY_POS_PROP = "inventory_pos"; + public static final String INVENTORY_AUTOSIZE_PROP = "inventory_autosize"; public static final String SINGLE_ACTION_INVENTORY = "single_action_inventory"; - public static final String TITLE_PROP="title"; - public static final String LOAD_GAMESTATE_PROP="load_gamestate"; - public static final String PLAY_RECORD_PROP="play_recording"; - public static final String TEST_SCENE_PROP="test_scene"; + public static final String TITLE_PROP = "title"; + public static final String LOAD_GAMESTATE_PROP = "load_gamestate"; + public static final String PLAY_RECORD_PROP = "play_recording"; + public static final String TEST_SCENE_PROP = "test_scene"; public static final String FORCE_RES_PROP = "force_res"; public static final String DEBUG_PROP = "debug"; public static final String CHAPTER_PROP = "chapter"; @@ -39,48 +39,49 @@ public class Config { public static final String FAST_LEAVE = "fast_leave"; public static final String AUTO_HIDE_TEXTS = "auto_hide_texts"; public static final String RESOLUTIONS = "resolutions"; + public static final String SHOW_HOTSPOTS = "show_hotspots"; public static final String PROPERTIES_FILENAME = "BladeEngine.properties"; private static Properties config = null; - + public static String getProperty(String key, String defaultValue) { - if(config == null) { + if (config == null) { load(); } - + return config.getProperty(key, defaultValue); } - + public static void load() { config = new Properties(); - + try { config.load(EngineAssetManager.getInstance().getAsset(PROPERTIES_FILENAME).reader()); } catch (Exception e) { EngineLogger.error("ERROR LOADING PROPERTIES: " + e.getMessage()); } } - + public static boolean getProperty(String key, boolean defaultValue) { boolean result = false; - + try { result = Boolean.parseBoolean(getProperty(key, String.valueOf(defaultValue))); } catch (Exception e) { } - + return result; } - + public static int getProperty(String key, int defaultValue) { int result = 0; - + try { result = Integer.parseInt(getProperty(key, String.valueOf(defaultValue))); } catch (Exception e) { } - + return result; } } From 73339c8ef70df0445d800b9a203b89b5a8614f3a Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Thu, 29 Aug 2019 21:28:16 +0200 Subject: [PATCH 034/147] Better asset loading in MoveToScene action. --- .../engine/actions/MoveToSceneAction.java | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/blade-engine/src/com/bladecoder/engine/actions/MoveToSceneAction.java b/blade-engine/src/com/bladecoder/engine/actions/MoveToSceneAction.java index 674f71532..88c5effd8 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/MoveToSceneAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/MoveToSceneAction.java @@ -27,60 +27,63 @@ @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); + InteractiveActor a = (InteractiveActor) s.getActor(actorId, 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; } - } From 57815a6b2d6c241d41a7c4ad4868b5d360229896 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Tue, 17 Sep 2019 12:13:02 +0200 Subject: [PATCH 035/147] In savegames, only save modified actor properties. --- .../assets-raw/editor/editor-icon.svg | 38 +++-------- adventure-editor/assets-raw/editor/title.svg | 27 ++++---- .../engine/actions/ActionFactory.java | 1 + .../engine/actions/SetDescAction.java | 62 ++++++++++++++++++ .../engine/actions/SetStateAction.java | 3 +- .../bladecoder/engine/model/BaseActor.java | 33 +++++++--- .../engine/model/CharacterActor.java | 65 ++++++++++++------- .../bladecoder/engine/model/DirtyProps.java | 6 ++ .../engine/model/InteractiveActor.java | 39 +++++++---- .../bladecoder/engine/model/SpriteActor.java | 61 ++++++++++++----- .../serialization/WorldSerialization.java | 1 - 11 files changed, 229 insertions(+), 107 deletions(-) create mode 100644 blade-engine/src/com/bladecoder/engine/actions/SetDescAction.java create mode 100644 blade-engine/src/com/bladecoder/engine/model/DirtyProps.java 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" /> - AdventureCOMPOSER 0) bbox.setVertices(verts); - } else { - } Vector2 pos = json.readValue("pos", Vector2.class, jsonData); @@ -140,8 +153,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..e655e0a82 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) { @@ -171,10 +177,8 @@ 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)); @@ -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/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/InteractiveActor.java b/blade-engine/src/com/bladecoder/engine/model/InteractiveActor.java index 51d807def..119ce830c 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 tween) { removeTween(tween.getClass()); - + tweens.add(tween); } @@ -323,24 +332,37 @@ public void write(Json json) { } 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 +376,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); @@ -374,7 +396,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); @@ -394,6 +416,9 @@ public void read(Json json, JsonValue jsonData) { 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/serialization/WorldSerialization.java b/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java index 537a79afb..b4deeb68e 100644 --- a/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java +++ b/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java @@ -181,7 +181,6 @@ public void saveModel(String chapterId) throws IOException { Json json = new BladeJson(w, Mode.MODEL); json.setOutputType(OutputType.javascript); - json.setSortFields(true); String s = null; From 85855ebaaee52404865070218bfa6d8dc6767925 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Tue, 17 Sep 2019 12:45:01 +0200 Subject: [PATCH 036/147] Ordered keys in json chapters and savegames. --- .../engine/model/AnimationRenderer.java | 7 ++++++- .../com/bladecoder/engine/model/Scene.java | 10 ++++++++-- .../serialization/WorldSerialization.java | 19 ++++++++++++++++--- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/blade-engine/src/com/bladecoder/engine/model/AnimationRenderer.java b/blade-engine/src/com/bladecoder/engine/model/AnimationRenderer.java index f79688fe2..05986fcec 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; @@ -379,7 +381,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); diff --git a/blade-engine/src/com/bladecoder/engine/model/Scene.java b/blade-engine/src/com/bladecoder/engine/model/Scene.java index b189462fa..698f6429a 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; @@ -699,7 +701,9 @@ public void write(Json json) { 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 +722,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); } diff --git a/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java b/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java index b4deeb68e..a5f2931b5 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; @@ -227,6 +229,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; @@ -258,13 +261,23 @@ public void write(Json json) { json.writeValue(Config.BLADE_ENGINE_VERSION_PROP, Config.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); + + 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()); From 625b113f3e69f63b87463d97b36ca72bb9eb6af7 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Tue, 17 Sep 2019 13:31:04 +0200 Subject: [PATCH 037/147] Ordered .properties case sensitive. --- .../engineeditor/common/I18NUtils.java | 86 +- .../common/OrderedProperties.java | 1033 +++++++++-------- .../engineeditor/model/I18NHandler.java | 18 +- .../engineeditor/model/Project.java | 6 +- 4 files changed, 586 insertions(+), 557 deletions(-) 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/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/model/I18NHandler.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/model/I18NHandler.java index 2c83f0797..dd6149eed 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 @@ -195,7 +195,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())); } } @@ -379,10 +379,10 @@ 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) + + if (idx != -1) key = key.substring(0, idx); - + while (i18nChapter.containsProperty(key.charAt(0) == I18N.PREFIX ? key.substring(1) : key)) key += '_'; @@ -392,10 +392,10 @@ public String getNotDuplicateKey(String key) { public String getNotDuplicateKeyWorld(String key) { // first delete all '_' at the end int idx = key.indexOf('_'); - - if(idx != -1) + + if (idx != -1) key = key.substring(0, idx); - + while (i18nWorld.containsProperty(key.charAt(0) == I18N.PREFIX ? key.substring(1) : key)) key += '_'; @@ -403,7 +403,7 @@ public String getNotDuplicateKeyWorld(String key) { } private void deleteUnusedKeys() { - ArrayList usedKeys = new ArrayList(); + ArrayList usedKeys = new ArrayList<>(); // SCENES for (Scene s : Ctx.project.getWorld().getScenes().values()) @@ -429,7 +429,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 a27160c24..31931d0ca 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 @@ -381,7 +381,7 @@ public void loadProject(File projectToLoad) throws IOException { // No need to load the chapter. It's loaded by the chapter combo. // loadChapter(world.getInitChapter()); - projectConfig = new OrderedPropertiesBuilder().withSuppressDateInComment(true).withOrdering().build(); + projectConfig = new OrderedPropertiesBuilder().withSuppressDateInComment(true).withOrderingCaseSensitive().build(); projectConfig.load(new FileInputStream(getAssetPath() + "/" + Config.PROPERTIES_FILENAME)); modified = false; @@ -475,7 +475,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(); @@ -541,7 +541,7 @@ 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")); From fb1ddf80f5efad5ae15f7dce959751a9cd420214 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Mon, 30 Sep 2019 18:20:13 +0200 Subject: [PATCH 038/147] Prepare for v3.2.0 --- CHANGELOG.md | 13 +++++++++++++ .../src/main/resources/versions.properties | 2 +- .../com/bladecoder/engine/model/TextManager.java | 5 +++-- .../engine/serialization/WorldSerialization.java | 2 +- gradle.properties | 2 +- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index caf348639..e176120bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [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. diff --git a/adventure-editor/src/main/resources/versions.properties b/adventure-editor/src/main/resources/versions.properties index b2cf357f1..5be2a7e6c 100644 --- a/adventure-editor/src/main/resources/versions.properties +++ b/adventure-editor/src/main/resources/versions.properties @@ -5,4 +5,4 @@ buildToolsVersion=28.0.3 libgdxVersion=1.9.10 roboVMGradlePluginVersion=2.3.7 roboVMVersion=2.3.7 -version=3.1.3-SNAPSHOT +version=3.2.0 diff --git a/blade-engine/src/com/bladecoder/engine/model/TextManager.java b/blade-engine/src/com/bladecoder/engine/model/TextManager.java index 7510f0a94..e7dcddae3 100644 --- a/blade-engine/src/com/bladecoder/engine/model/TextManager.java +++ b/blade-engine/src/com/bladecoder/engine/model/TextManager.java @@ -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/serialization/WorldSerialization.java b/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java index a5f2931b5..9b91f321c 100644 --- a/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java +++ b/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java @@ -42,7 +42,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; diff --git a/gradle.properties b/gradle.properties index 0d8e21b2c..d7568b3d4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=3.1.3-SNAPSHOT +version=3.2.0 libgdxVersion=1.9.10 roboVMVersion=2.3.7 roboVMGradlePluginVersion=2.3.7 From db79ce556ba4f75782a6f0b32f44d0bfb1e184d2 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Mon, 30 Sep 2019 18:30:43 +0200 Subject: [PATCH 039/147] Fix maven sign error. --- blade-engine/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blade-engine/build.gradle b/blade-engine/build.gradle index d720a9411..a19425e52 100644 --- a/blade-engine/build.gradle +++ b/blade-engine/build.gradle @@ -61,7 +61,7 @@ task sourcesJar(type: Jar) { } artifacts { - archives jar + //archives jar archives enginedocJar archives sourcesJar } From c5e828c0b978d75551b88f3b14fd6136c14333fb Mon Sep 17 00:00:00 2001 From: rgarcia Date: Mon, 30 Sep 2019 18:39:10 +0200 Subject: [PATCH 040/147] Fix maven sign error #2. --- blade-engine-spine-plugin/build.gradle | 4 ++-- blade-engine/build.gradle | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/blade-engine-spine-plugin/build.gradle b/blade-engine-spine-plugin/build.gradle index 33121732f..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' @@ -52,7 +52,7 @@ task sourcesJar(type: Jar) { } artifacts { - archives jar + //archives jar archives enginedocJar archives sourcesJar } diff --git a/blade-engine/build.gradle b/blade-engine/build.gradle index a19425e52..773ced8b5 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' From 0c40a08d9587f0a463f5b9e877c956b04e68836f Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Wed, 2 Oct 2019 00:12:09 +0200 Subject: [PATCH 041/147] Fix saving modified position. --- .../src/main/resources/versions.properties | 2 +- .../bladecoder/engine/model/BaseActor.java | 10 ++++++---- .../engine/model/CharacterActor.java | 4 ++-- .../engine/model/InteractiveActor.java | 4 ++-- .../engine/model/ObstacleActor.java | 18 +++++++++-------- .../com/bladecoder/engine/model/Scene.java | 6 +++--- .../bladecoder/engine/model/SpriteActor.java | 20 +++++++++---------- .../engine/model/WalkZoneActor.java | 7 ++++--- gradle.properties | 2 +- 9 files changed, 39 insertions(+), 34 deletions(-) diff --git a/adventure-editor/src/main/resources/versions.properties b/adventure-editor/src/main/resources/versions.properties index 5be2a7e6c..bb56d100a 100644 --- a/adventure-editor/src/main/resources/versions.properties +++ b/adventure-editor/src/main/resources/versions.properties @@ -5,4 +5,4 @@ buildToolsVersion=28.0.3 libgdxVersion=1.9.10 roboVMGradlePluginVersion=2.3.7 roboVMVersion=2.3.7 -version=3.2.0 +version=3.2.1-SNAPSHOT diff --git a/blade-engine/src/com/bladecoder/engine/model/BaseActor.java b/blade-engine/src/com/bladecoder/engine/model/BaseActor.java index 36bb5d124..ce4d692f9 100644 --- a/blade-engine/src/com/bladecoder/engine/model/BaseActor.java +++ b/blade-engine/src/com/bladecoder/engine/model/BaseActor.java @@ -32,8 +32,8 @@ 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; @@ -102,8 +102,10 @@ public float getY() { } public void setPosition(float x, float y) { - bbox.setPosition(x, y); - setDirtyProp(DirtyProps.POS); + if (x != bbox.getX() || y != bbox.getY()) { + bbox.setPosition(x, y); + setDirtyProp(DirtyProps.POS); + } } public String getInitScene() { diff --git a/blade-engine/src/com/bladecoder/engine/model/CharacterActor.java b/blade-engine/src/com/bladecoder/engine/model/CharacterActor.java index e655e0a82..93864f85c 100644 --- a/blade-engine/src/com/bladecoder/engine/model/CharacterActor.java +++ b/blade-engine/src/com/bladecoder/engine/model/CharacterActor.java @@ -131,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); } @@ -183,7 +183,7 @@ public void startWalkAnim(Vector2 p0, Vector2 pf) { 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) { diff --git a/blade-engine/src/com/bladecoder/engine/model/InteractiveActor.java b/blade-engine/src/com/bladecoder/engine/model/InteractiveActor.java index 119ce830c..dae7999c7 100644 --- a/blade-engine/src/com/bladecoder/engine/model/InteractiveActor.java +++ b/blade-engine/src/com/bladecoder/engine/model/InteractiveActor.java @@ -68,7 +68,7 @@ public String getLayer() { * @return Is visible and has interaction */ public boolean canInteract() { - return interaction && visible; + return interaction && isVisible(); } public boolean getInteraction() { @@ -108,7 +108,7 @@ public void update(float delta) { if (scene != null) player = scene.getPlayer(); - if (visible && player != null) { + if (isVisible() && player != null) { boolean hit = hit(player.getX(), player.getY()); if (!hit && playerInside) { // the player leaves 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/Scene.java b/blade-engine/src/com/bladecoder/engine/model/Scene.java index 698f6429a..47910d19f 100644 --- a/blade-engine/src/com/bladecoder/engine/model/Scene.java +++ b/blade-engine/src/com/bladecoder/engine/model/Scene.java @@ -798,9 +798,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); diff --git a/blade-engine/src/com/bladecoder/engine/model/SpriteActor.java b/blade-engine/src/com/bladecoder/engine/model/SpriteActor.java index b661fd04a..cb78277e4 100644 --- a/blade-engine/src/com/bladecoder/engine/model/SpriteActor.java +++ b/blade-engine/src/com/bladecoder/engine/model/SpriteActor.java @@ -90,7 +90,7 @@ public void setBboxFromRenderer(boolean v) { this.bboxFromRenderer = v; if (v) - renderer.updateBboxFromRenderer(bbox); + renderer.updateBboxFromRenderer(getBBox()); else renderer.updateBboxFromRenderer(null); @@ -136,10 +136,10 @@ public void setScale(float scaleX, float scaleY) { 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); @@ -148,7 +148,7 @@ public void setScale(float scaleX, float scaleY) { public void setRot(float rot) { this.rot = rot; - bbox.setRotation(rot); + getBBox().setRotation(rot); setDirtyProp(DirtyProps.ROT); } @@ -160,7 +160,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++) { @@ -307,7 +307,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 @@ -323,12 +323,12 @@ 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); } @@ -412,7 +412,7 @@ 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); 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/gradle.properties b/gradle.properties index d7568b3d4..b45b42201 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=3.2.0 +version=3.2.1-SNAPSHOT libgdxVersion=1.9.10 roboVMVersion=2.3.7 roboVMGradlePluginVersion=2.3.7 From 21dcf07bd5fd66d1d678cb69f992981b58ff01b8 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Thu, 3 Oct 2019 17:57:35 +0200 Subject: [PATCH 042/147] Allow null when setting style. --- blade-engine/src/com/bladecoder/engine/ui/InventoryUI.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blade-engine/src/com/bladecoder/engine/ui/InventoryUI.java b/blade-engine/src/com/bladecoder/engine/ui/InventoryUI.java index 3d2bbc18e..39b33a190 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/InventoryUI.java +++ b/blade-engine/src/com/bladecoder/engine/ui/InventoryUI.java @@ -234,7 +234,7 @@ public void resize(int width, int height) { } public void setStyle(String name) { - style = sceneScreen.getUI().getSkin().get(name, InventoryUIStyle.class); + style = sceneScreen.getUI().getSkin().get(name == null ? "default" : name, InventoryUIStyle.class); } public void retrieveAssets(TextureAtlas atlas) { From af22745a712b55ca2a5bd489f15c18719ce6c89c Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Thu, 17 Oct 2019 21:09:52 +0200 Subject: [PATCH 043/147] Some checks in recorder. --- .../com/bladecoder/engine/ui/Recorder.java | 82 ++++++++++--------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/blade-engine/src/com/bladecoder/engine/ui/Recorder.java b/blade-engine/src/com/bladecoder/engine/ui/Recorder.java index 768f847f4..a919770f2 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/Recorder.java +++ b/blade-engine/src/com/bladecoder/engine/ui/Recorder.java @@ -77,57 +77,61 @@ public void update(float delta) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("RECORDER (").append(pos).append(") - "); - // while (playing && v.time < time) { - if (playing && v.time < time && !w.inCutMode()) { + // check preconditions + if (!playing || v.time > time || w.inCutMode()) + return; - if (v.verb == null) { - if (v.pos == null) { // DIALOG OPTION - w.selectDialogOption(v.dialogOption); + if (v.verb == null) { + if (v.pos == null) { // DIALOG OPTION + if (!w.hasDialogOptions() || w.getDialogOptions().size() <= v.dialogOption) { + EngineLogger.error("PLAYING ERROR: No dialog options to select: " + v.dialogOption); + } - stringBuilder.append(" SELECT DIALOG OPTION: ").append(v.dialogOption); - } else { // GOTO - s.getPlayer().goTo(v.pos, null, false); + w.selectDialogOption(v.dialogOption); - stringBuilder.append(" GOTO ").append(v.pos.x).append(',').append(v.pos.y); - } - } else { + stringBuilder.append(" SELECT DIALOG OPTION: ").append(v.dialogOption); + } else { // GOTO + s.getPlayer().goTo(v.pos, null, false); - stringBuilder.append(v.verb); + stringBuilder.append(" GOTO ").append(v.pos.x).append(',').append(v.pos.y); + } + } else { - if (v.verb.equals("SAVEGAME")) { - // SPECIAL VERB TO SAVE THE GAME - stringBuilder.append(v.target); - try { - w.getSerializer().saveGameState(v.target, true); - } catch (IOException e) { - EngineLogger.error("Couldn't save game: " + v.target + " : " + e.getMessage()); - } - } else { + stringBuilder.append(v.verb); - InteractiveActor a = (InteractiveActor) s.getActor(v.actorId, true); + if (v.verb.equals("SAVEGAME")) { + // SPECIAL VERB TO SAVE THE GAME + stringBuilder.append(v.target); + try { + w.getSerializer().saveGameState(v.target, true); + } catch (IOException e) { + EngineLogger.error("Couldn't save game: " + v.target + " : " + e.getMessage()); + } + } else { - if (a != null) { - stringBuilder.append(' ').append(v.actorId); + InteractiveActor a = (InteractiveActor) s.getActor(v.actorId, true); - if (v.target != null) { - stringBuilder.append(" with ").append(v.target); - } + if (a != null) { + stringBuilder.append(' ').append(v.actorId); - a.runVerb(v.verb, v.target); - } else - EngineLogger.error("PLAYING ERROR: BaseActor not found: " + v.actorId); - } + if (v.target != null) { + stringBuilder.append(" with ").append(v.target); + } + + a.runVerb(v.verb, v.target); + } else + EngineLogger.error("PLAYING ERROR: BaseActor not found: " + v.actorId); } + } - EngineLogger.debug(stringBuilder.toString()); + EngineLogger.debug(stringBuilder.toString()); - time = 0; - pos++; - if (pos >= list.size()) { - setPlaying(false); - } else { - v = list.get(pos); - } + time = 0; + pos++; + if (pos >= list.size()) { + setPlaying(false); + } else { + v = list.get(pos); } } } From 9337bc801215f5a132df35eeef0cb1e0b52d3f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Garc=C3=ADa?= Date: Fri, 18 Oct 2019 15:13:46 +0200 Subject: [PATCH 044/147] Hide dialog when no options. --- blade-engine/src/com/bladecoder/engine/ui/DialogUI.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/blade-engine/src/com/bladecoder/engine/ui/DialogUI.java b/blade-engine/src/com/bladecoder/engine/ui/DialogUI.java index ebe7f6afc..2ddc63afc 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/DialogUI.java +++ b/blade-engine/src/com/bladecoder/engine/ui/DialogUI.java @@ -136,6 +136,7 @@ private void show() { choices = ui.getWorld().getDialogOptions(); if (choices.size() == 0) { + setVisible(false); return; } else if (style.autoselect && choices.size() == 1) { // If only has one option, autoselect it @@ -150,6 +151,7 @@ public void run() { } }); + setVisible(false); return; } From 93c7d8d4c79bdc3174441c7402b6e6f30e64d237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Garc=C3=ADa?= Date: Fri, 18 Oct 2019 15:14:18 +0200 Subject: [PATCH 045/147] Update blade-ink to v0.7.2 --- adventure-editor/src/main/resources/versions.properties | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/adventure-editor/src/main/resources/versions.properties b/adventure-editor/src/main/resources/versions.properties index bb56d100a..79fc49419 100644 --- a/adventure-editor/src/main/resources/versions.properties +++ b/adventure-editor/src/main/resources/versions.properties @@ -1,6 +1,6 @@ androidAPILevel=28 androidGradlePluginVersion=3.4.1 -bladeInkVersion=0.7.0 +bladeInkVersion=0.7.2 buildToolsVersion=28.0.3 libgdxVersion=1.9.10 roboVMGradlePluginVersion=2.3.7 diff --git a/gradle.properties b/gradle.properties index b45b42201..50b2c1a8c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ roboVMGradlePluginVersion=2.3.7 buildToolsVersion=28.0.3 androidAPILevel=28 androidGradlePluginVersion=3.4.1 -bladeInkVersion=0.7.0 +bladeInkVersion=0.7.2 From 89029b345320486f3aaf98cffc9348448b551d84 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Mon, 21 Oct 2019 00:07:44 +0200 Subject: [PATCH 046/147] Fix load action bug on InkManager. --- .../src/com/bladecoder/engine/ink/InkManager.java | 7 +++++++ .../src/com/bladecoder/engine/ui/Recorder.java | 11 ++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/blade-engine/src/com/bladecoder/engine/ink/InkManager.java b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java index fa82ef6f6..8b9b99e7f 100644 --- a/blade-engine/src/com/bladecoder/engine/ink/InkManager.java +++ b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java @@ -303,6 +303,13 @@ private void processCommand(HashMap params, String line) { Action action; try { + + 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); diff --git a/blade-engine/src/com/bladecoder/engine/ui/Recorder.java b/blade-engine/src/com/bladecoder/engine/ui/Recorder.java index a919770f2..bb3390e86 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/Recorder.java +++ b/blade-engine/src/com/bladecoder/engine/ui/Recorder.java @@ -84,12 +84,13 @@ public void update(float delta) { if (v.verb == null) { if (v.pos == null) { // DIALOG OPTION if (!w.hasDialogOptions() || w.getDialogOptions().size() <= v.dialogOption) { - EngineLogger.error("PLAYING ERROR: No dialog options to select: " + v.dialogOption); - } - - w.selectDialogOption(v.dialogOption); + EngineLogger.error("PLAYING ERROR: No dialog options to select. Size: " + + w.getDialogOptions().size() + " select: " + v.dialogOption); + } else { + w.selectDialogOption(v.dialogOption); - stringBuilder.append(" SELECT DIALOG OPTION: ").append(v.dialogOption); + stringBuilder.append(" SELECT DIALOG OPTION: ").append(v.dialogOption); + } } else { // GOTO s.getPlayer().goTo(v.pos, null, false); From e2cfbce7acba053ca65fa79d7b87e074dfad99d9 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Tue, 22 Oct 2019 13:55:28 +0200 Subject: [PATCH 047/147] Better spine skins handling. --- .../com/bladecoder/engine/spine/SpineRenderer.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 fd4cbedad..f44f0e5f1 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 @@ -320,7 +320,7 @@ public void setSkin(String skin) { sce.skeleton.setSkin((Skin) null); } - sce.skeleton.setSlotsToSetupPose(); + // sce.skeleton.setSlotsToSetupPose(); } this.skin = skin; @@ -459,6 +459,11 @@ public AnimationState getCurrentAnimationState() { 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); @@ -670,9 +675,6 @@ public void retrieveAssets() { } else if (initAnimation != null) { startAnimation(initAnimation, Tween.Type.SPRITE_DEFINED, 1, null); } - - setSkin(skin); - computeBbox(); } @Override From b53c2557f54bed8e69c472950a742412029cc206 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Wed, 23 Oct 2019 19:36:09 +0200 Subject: [PATCH 048/147] Better multi inventory support in actions. --- .../engine/actions/DropItemAction.java | 44 +++++++++++++------ .../engine/actions/IfAttrAction.java | 19 +++++++- .../com/bladecoder/engine/ink/InkManager.java | 2 +- 3 files changed, 48 insertions(+), 17 deletions(-) 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/IfAttrAction.java b/blade-engine/src/com/bladecoder/engine/actions/IfAttrAction.java index 60270e85e..ab47fbe14 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/IfAttrAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/IfAttrAction.java @@ -18,6 +18,7 @@ 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; @@ -86,12 +87,26 @@ 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(a.getId()); if ((val && item == null) || (!val && item != null)) { gotoElse(cb); diff --git a/blade-engine/src/com/bladecoder/engine/ink/InkManager.java b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java index 8b9b99e7f..2b5a8f42b 100644 --- a/blade-engine/src/com/bladecoder/engine/ink/InkManager.java +++ b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java @@ -298,7 +298,7 @@ private void processCommand(HashMap params, String line) { } // 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; From f8ef0b8f34d60a2d0ac525b10c09e040a1dafbfd Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Fri, 25 Oct 2019 15:53:07 +0200 Subject: [PATCH 049/147] Add inventory param to PickUp action. --- .../engine/actions/PickUpAction.java | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) 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; From c0b58de2f926d9aec6a96105df4551a67f15f3b4 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Mon, 28 Oct 2019 19:57:46 +0100 Subject: [PATCH 050/147] Clean I18N util. --- .../common/EditorCommandExecutor.java | 7 ++- .../engineeditor/model/I18NHandler.java | 54 ++++++++++++++----- 2 files changed, 46 insertions(+), 15 deletions(-) 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/model/I18NHandler.java b/adventure-editor/src/main/java/com/bladecoder/engineeditor/model/I18NHandler.java index dd6149eed..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; @@ -377,29 +378,54 @@ 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('_'); + // first delete all numbers at the end to avoid always growing keys + String key2 = key.replaceAll("\\d*$", ""); - if (idx != -1) - key = key.substring(0, idx); + if (key2.isEmpty()) + key2 = key; - while (i18nChapter.containsProperty(key.charAt(0) == I18N.PREFIX ? key.substring(1) : key)) - key += '_'; + int i = 0; - return key; + 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('_'); + // 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; + } - if (idx != -1) - key = key.substring(0, idx); + return key2; + } + + public void cleanI18N() { + Map scenes = Ctx.project.getWorld().getScenes(); + for (Scene scn : scenes.values()) { + Ctx.project.getI18N().putTranslationsInElement(scn); + } - while (i18nWorld.containsProperty(key.charAt(0) == I18N.PREFIX ? key.substring(1) : key)) - key += '_'; + // i18nChapter = new + // OrderedPropertiesBuilder().withSuppressDateInComment(true).withOrdering().build(); + deleteUnusedKeys(); + + for (Scene scn : scenes.values()) { + Ctx.project.getI18N().extractStrings(scn); + } - return key; + Ctx.project.setModified(); } private void deleteUnusedKeys() { From 4913e8e9950633936cb414fca6bac0ba1b713ce1 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Thu, 31 Oct 2019 10:34:59 +0100 Subject: [PATCH 051/147] Delete previous anim when calling CameraAction. --- .../engine/actions/CameraAction.java | 54 ++++----- .../bladecoder/engine/model/SceneCamera.java | 109 +++++++++--------- 2 files changed, 85 insertions(+), 78 deletions(-) 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/model/SceneCamera.java b/blade-engine/src/com/bladecoder/engine/model/SceneCamera.java index 88a23d017..729aceba0 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,101 @@ 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.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 +145,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 +157,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 +167,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 +184,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); } - } + } } From 257ee425b2916d5be8b4c055dae18f584c59970a Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sat, 2 Nov 2019 21:46:09 +0100 Subject: [PATCH 052/147] Small bug fixes. --- .../engineeditor/ui/EditSceneDialog.java | 51 ++++++++++--------- .../engine/actions/ScaleAction.java | 23 ++++----- .../engine/actions/SetActorAttrAction.java | 17 +++---- .../engine/anim/SpriteAlphaTween.java | 29 ++++++----- .../engine/anim/SpriteTintTween.java | 31 ++++++----- .../engine/model/AtlasRenderer.java | 2 +- .../bladecoder/engine/model/SceneCamera.java | 2 +- .../bladecoder/engine/model/SpriteActor.java | 1 - 8 files changed, 83 insertions(+), 73 deletions(-) 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/blade-engine/src/com/bladecoder/engine/actions/ScaleAction.java b/blade-engine/src/com/bladecoder/engine/actions/ScaleAction.java index a85352ed3..41266b80d 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) { + public boolean run(VerbRunner cb) { SpriteActor a = (SpriteActor) w.getCurrentScene().getActor(actor, false); - + 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/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/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/model/AtlasRenderer.java b/blade-engine/src/com/bladecoder/engine/model/AtlasRenderer.java index 8d53f31bf..020de1877 100644 --- a/blade-engine/src/com/bladecoder/engine/model/AtlasRenderer.java +++ b/blade-engine/src/com/bladecoder/engine/model/AtlasRenderer.java @@ -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/SceneCamera.java b/blade-engine/src/com/bladecoder/engine/model/SceneCamera.java index 729aceba0..5124b7c8f 100644 --- a/blade-engine/src/com/bladecoder/engine/model/SceneCamera.java +++ b/blade-engine/src/com/bladecoder/engine/model/SceneCamera.java @@ -91,7 +91,7 @@ public void setScrollingDimensions(float w, float h) { public void update(float delta) { if (cameraTween != null) { cameraTween.update(delta); - if (cameraTween.isComplete()) { + if (cameraTween != null && cameraTween.isComplete()) { cameraTween = null; } } diff --git a/blade-engine/src/com/bladecoder/engine/model/SpriteActor.java b/blade-engine/src/com/bladecoder/engine/model/SpriteActor.java index cb78277e4..00b2bd4eb 100644 --- a/blade-engine/src/com/bladecoder/engine/model/SpriteActor.java +++ b/blade-engine/src/com/bladecoder/engine/model/SpriteActor.java @@ -123,7 +123,6 @@ public Color getTint() { public void setTint(Color tint) { this.tint = tint; - setDirtyProp(DirtyProps.TINT); } From 68e0a13abef3f2b623645fd85bc84d0022cd369e Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Mon, 4 Nov 2019 10:34:50 +0100 Subject: [PATCH 053/147] Use PolygonUtils::isPointInside on IfActorAttr to avoid precision errors when using with goTo --- .../engine/actions/IfAttrAction.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/blade-engine/src/com/bladecoder/engine/actions/IfAttrAction.java b/blade-engine/src/com/bladecoder/engine/actions/IfAttrAction.java index ab47fbe14..c4855efb9 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/IfAttrAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/IfAttrAction.java @@ -25,6 +25,7 @@ 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 { @@ -58,14 +59,13 @@ 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"); + BaseActor a = s.getActor(actorId, true); + + if (a == null) { + EngineLogger.error(getClass() + "- No not found: " + actorId); return false; } - BaseActor a = s.getActor(actorId, true); - if (attr.equals(ActorAttribute.STATE) && a instanceof InteractiveActor) { InteractiveActor ia = (InteractiveActor) a; if (!ActionUtils.compareNullStr(value, ia.getState())) { @@ -153,12 +153,13 @@ public boolean run(VerbRunner cb) { } } else if (attr.equals(ActorAttribute.INSIDE)) { BaseActor insideActor = w.getCurrentScene().getActor(value, false); - boolean inside = false; - if (a != null && insideActor != null) - inside = insideActor.getBBox().contains(a.getX(), a.getY()); - else + 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); From 4c41666d2c4356bd2f6cb5d506bb9252124f07b1 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Thu, 7 Nov 2019 17:12:25 +0100 Subject: [PATCH 054/147] Check scene name also in CURRENT_SCENE ifSceneAttr. --- .../engine/actions/IfSceneAttrAction.java | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/blade-engine/src/com/bladecoder/engine/actions/IfSceneAttrAction.java b/blade-engine/src/com/bladecoder/engine/actions/IfSceneAttrAction.java index 2a2db5009..f8f23bf3e 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,26 @@ 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 (!ActionUtils.compareNullStr(value, scn) && !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; From 8b4e99f2add2852ba751e15b0804ab9d8aec87c7 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Thu, 7 Nov 2019 18:13:08 +0100 Subject: [PATCH 055/147] Bubble positioning parameter on ui.json --- .../src/com/bladecoder/engine/ui/TextManagerUI.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/blade-engine/src/com/bladecoder/engine/ui/TextManagerUI.java b/blade-engine/src/com/bladecoder/engine/ui/TextManagerUI.java index b9770053b..64fe7877c 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/TextManagerUI.java +++ b/blade-engine/src/com/bladecoder/engine/ui/TextManagerUI.java @@ -191,11 +191,11 @@ public void draw(Batch batch, float alpha) { if (style.talkBubble != null) { float scale = DPIUtils.getTouchMinSize() * style.bubbleSize / 4 / style.talkBubble.getMinHeight(); - float bubbleX = unprojectTmp.x - style.talkBubble.getMinWidth() * scale / 2; + float bubbleX = unprojectTmp.x - style.talkBubble.getMinWidth() * scale / 2 + style.bubbledx; // check screen exit bubbleX = Math.max(bubbleX, getX() + PADDING - style.talkBubble.getMinWidth() * scale / 2); bubbleX = Math.min(bubbleX, getX() + getWidth() - PADDING - style.talkBubble.getMinWidth() * scale / 2); - float bubbleY = getY() - style.talkBubble.getMinHeight() * scale + 2; + float bubbleY = getY() - style.talkBubble.getMinHeight() * scale + style.bubbledy; style.talkBubble.draw(batch, bubbleX, bubbleY, style.talkBubble.getMinWidth() * scale, style.talkBubble.getMinHeight() * scale); @@ -253,6 +253,8 @@ static public class TextManagerUIStyle { public Color defaultColor; public float subtitlePosPercent = 0.90f; public float bubbleSize = 1f; + public float bubbledx = 0; + public float bubbledy = 0; public int maxCharWidth = 80; public int maxTalkCharWidth = 35; @@ -267,6 +269,8 @@ public TextManagerUIStyle(TextManagerUIStyle style) { defaultColor = style.defaultColor; subtitlePosPercent = style.subtitlePosPercent; bubbleSize = style.bubbleSize; + bubbledx = style.bubbledx; + bubbledy = style.bubbledy; maxCharWidth = style.maxCharWidth; maxTalkCharWidth = style.maxTalkCharWidth; } From fc481ffc3eb02736ba4e81e1120ebda8aa603f63 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Thu, 7 Nov 2019 19:10:46 +0100 Subject: [PATCH 056/147] Prepare for v3.2.1 --- CHANGELOG.md | 9 ++++++++- adventure-editor/src/main/resources/versions.properties | 2 +- gradle.properties | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e176120bd..fab236955 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [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. @@ -13,7 +21,6 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Updated libgdx to v1.9.10 - Updated Spine to v3.8. - Update gradle to v5.4.1 -- ## [3.1.2] diff --git a/adventure-editor/src/main/resources/versions.properties b/adventure-editor/src/main/resources/versions.properties index 79fc49419..aeb00c525 100644 --- a/adventure-editor/src/main/resources/versions.properties +++ b/adventure-editor/src/main/resources/versions.properties @@ -5,4 +5,4 @@ buildToolsVersion=28.0.3 libgdxVersion=1.9.10 roboVMGradlePluginVersion=2.3.7 roboVMVersion=2.3.7 -version=3.2.1-SNAPSHOT +version=3.2.1 diff --git a/gradle.properties b/gradle.properties index 50b2c1a8c..267120eb2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=3.2.1-SNAPSHOT +version=3.2.1 libgdxVersion=1.9.10 roboVMVersion=2.3.7 roboVMGradlePluginVersion=2.3.7 From bb7f5d88d75b93597ee007acefe4e9d03a7d5aef Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Wed, 13 Nov 2019 20:12:39 +0100 Subject: [PATCH 057/147] Remove timers when cancel Ink running actions. --- blade-engine/src/com/bladecoder/engine/ink/InkManager.java | 5 +++++ gradle.properties | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/blade-engine/src/com/bladecoder/engine/ink/InkManager.java b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java index 2b5a8f42b..ae97fa02f 100644 --- a/blade-engine/src/com/bladecoder/engine/ink/InkManager.java +++ b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java @@ -183,6 +183,8 @@ private void continueMaximally() { waitIfNotLoaded(); String line = null; + // Cancel possible pending timer + w.getCurrentScene().getTimers().removeTimerWithCb(this); actions.clear(); HashMap currentLineParams = new HashMap<>(); @@ -502,6 +504,9 @@ public void setIP(int ip) { @Override public void cancel() { + // Cancel possible pending timer + w.getCurrentScene().getTimers().removeTimerWithCb(this); + ArrayList actions = getActions(); for (Action c : actions) { diff --git a/gradle.properties b/gradle.properties index 267120eb2..409c9c1d2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=3.2.1 +version=3.2.2-SNAPSHOT libgdxVersion=1.9.10 roboVMVersion=2.3.7 roboVMGradlePluginVersion=2.3.7 From 6ceecc1a0867669c3f0cfc56cf90369aa7112667 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Mon, 18 Nov 2019 19:54:16 +0100 Subject: [PATCH 058/147] Better translation of ink lines. --- .../com/bladecoder/engineeditor/common/ModelTools.java | 3 ++- adventure-editor/src/main/resources/versions.properties | 2 +- .../src/com/bladecoder/engine/ink/InkManager.java | 9 ++++++++- 3 files changed, 11 insertions(+), 3 deletions(-) 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 7c10736f2..9700547e7 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; @@ -530,7 +531,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/resources/versions.properties b/adventure-editor/src/main/resources/versions.properties index aeb00c525..a2339cbe3 100644 --- a/adventure-editor/src/main/resources/versions.properties +++ b/adventure-editor/src/main/resources/versions.properties @@ -5,4 +5,4 @@ buildToolsVersion=28.0.3 libgdxVersion=1.9.10 roboVMGradlePluginVersion=2.3.7 roboVMVersion=2.3.7 -version=3.2.1 +version=3.2.2-SNAPSHOT diff --git a/blade-engine/src/com/bladecoder/engine/ink/InkManager.java b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java index ae97fa02f..ea85c0ee2 100644 --- a/blade-engine/src/com/bladecoder/engine/ink/InkManager.java +++ b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java @@ -34,6 +34,7 @@ import com.bladecoder.ink.runtime.Story; public class InkManager implements VerbRunner, 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 = ","; @@ -124,7 +125,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, 10); + translated += i18n.getString(k2); + if (k.length() > 10) { + String trailing = k.substring(10); + translated += trailing; + } } catch (Exception e) { EngineLogger.error("MISSING TRANSLATION KEY: " + key); return key; From 5a7f9d89a623eee9bbaeffb4b1fdb9545f8a2585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Garc=C3=ADa?= Date: Wed, 20 Nov 2019 20:27:42 +0100 Subject: [PATCH 059/147] Several Worlds can be loaded simultaneously. --- .../engineeditor/ui/EditActorDialog.java | 1 + .../com/bladecoder/engine/BladeEngine.java | 28 +++---- .../src/com/bladecoder/engine/i18n/I18N.java | 44 +++++------ .../com/bladecoder/engine/ink/InkManager.java | 2 +- .../engine/model/ActorRenderer.java | 15 ++-- .../engine/model/AnimationRenderer.java | 16 +++- .../engine/model/AtlasRenderer.java | 2 +- .../engine/model/ImageRenderer.java | 2 +- .../engine/model/ParticleRenderer.java | 6 ++ .../bladecoder/engine/model/SpriteActor.java | 2 + .../bladecoder/engine/model/TextManager.java | 2 +- .../bladecoder/engine/model/TextRenderer.java | 11 ++- .../com/bladecoder/engine/model/World.java | 6 ++ .../serialization/WorldSerialization.java | 7 +- .../com/bladecoder/engine/ui/DialogUI.java | 26 +++---- .../com/bladecoder/engine/ui/HelpScreen.java | 23 +++--- .../bladecoder/engine/ui/InventoryButton.java | 14 ++-- .../com/bladecoder/engine/ui/InventoryUI.java | 8 +- .../bladecoder/engine/ui/LoadSaveScreen.java | 25 +++--- .../com/bladecoder/engine/ui/MenuScreen.java | 18 +++-- .../src/com/bladecoder/engine/ui/PieMenu.java | 2 +- .../com/bladecoder/engine/ui/PieMenu2.java | 76 +++++++++---------- .../com/bladecoder/engine/ui/SceneScreen.java | 5 ++ .../bladecoder/engine/ui/TextManagerUI.java | 18 +++-- .../src/com/bladecoder/engine/ui/UI.java | 7 +- .../ui/defaults/DefaultSceneScreen.java | 49 ++++++------ .../engine/ui/defaults/ScenePointer.java | 9 ++- .../engine/ui/retro/RetroSceneScreen.java | 14 ++-- .../bladecoder/engine/ui/retro/VerbUI.java | 58 +++++++------- 29 files changed, 279 insertions(+), 217 deletions(-) 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..836dfbcba 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 @@ -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/blade-engine/src/com/bladecoder/engine/BladeEngine.java b/blade-engine/src/com/bladecoder/engine/BladeEngine.java index d20757564..e6b20299a 100644 --- a/blade-engine/src/com/bladecoder/engine/BladeEngine.java +++ b/blade-engine/src/com/bladecoder/engine/BladeEngine.java @@ -77,20 +77,20 @@ 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) { + + if (baseFolder != null) { EngineAssetManager.setAssetFolder(baseFolder); Config.load(); } - + try { world.loadWorldDesc(); } catch (Exception e) { @@ -98,7 +98,7 @@ public void loadGame(String baseFolder) { EngineLogger.error("EXITING: " + e.getMessage()); Gdx.app.exit(); } - + ui = new UI(world); } @@ -155,7 +155,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); @@ -171,7 +171,7 @@ public void create() { ui.getRecorder().setFilename(recordName); ui.getRecorder().load(); ui.getRecorder().setPlaying(true); - + ui.setCurrentScreen(UI.Screens.SCENE_SCREEN); } } @@ -185,9 +185,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 +207,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 +217,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/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/InkManager.java b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java index ea85c0ee2..c60f0d9f2 100644 --- a/blade-engine/src/com/bladecoder/engine/ink/InkManager.java +++ b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java @@ -111,7 +111,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) { 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 05986fcec..4a2b2c906 100644 --- a/blade-engine/src/com/bladecoder/engine/model/AnimationRenderer.java +++ b/blade-engine/src/com/bladecoder/engine/model/AnimationRenderer.java @@ -43,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; @@ -53,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; } @@ -122,6 +124,7 @@ public String getCurrentAnimationId() { } + @Override public String toString() { StringBuilder sb = new StringBuilder(super.toString()); @@ -139,6 +142,7 @@ public String toString() { return sb.toString(); } + @Override public void updateBboxFromRenderer(Polygon bbox) { this.bbox = bbox; computeBbox(); @@ -352,8 +356,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 */ @@ -422,4 +425,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 020de1877..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); diff --git a/blade-engine/src/com/bladecoder/engine/model/ImageRenderer.java b/blade-engine/src/com/bladecoder/engine/model/ImageRenderer.java index f784519c4..640b5ff3d 100644 --- a/blade-engine/src/com/bladecoder/engine/model/ImageRenderer.java +++ b/blade-engine/src/com/bladecoder/engine/model/ImageRenderer.java @@ -217,7 +217,7 @@ private void retrieveSource(String source) { } 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/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/SpriteActor.java b/blade-engine/src/com/bladecoder/engine/model/SpriteActor.java index 00b2bd4eb..121ad6ca4 100644 --- a/blade-engine/src/com/bladecoder/engine/model/SpriteActor.java +++ b/blade-engine/src/com/bladecoder/engine/model/SpriteActor.java @@ -388,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; diff --git a/blade-engine/src/com/bladecoder/engine/model/TextManager.java b/blade-engine/src/com/bladecoder/engine/model/TextManager.java index e7dcddae3..30593ad06 100644 --- a/blade-engine/src/com/bladecoder/engine/model/TextManager.java +++ b/blade-engine/src/com/bladecoder/engine/model/TextManager.java @@ -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"); diff --git a/blade-engine/src/com/bladecoder/engine/model/TextRenderer.java b/blade-engine/src/com/bladecoder/engine/model/TextRenderer.java index 438e63557..328bd3421 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; @@ -318,7 +320,7 @@ public void retrieveAssets() { 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; @@ -376,4 +378,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/World.java b/blade-engine/src/com/bladecoder/engine/model/World.java index 455052c1b..3d0a345ee 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; @@ -348,6 +350,10 @@ public AssetState getAssetState() { return assetState; } + public I18N getI18N() { + return i18n; + } + public Dialog getCurrentDialog() { return currentDialog; } diff --git a/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java b/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java index 9b91f321c..4ef0f213f 100644 --- a/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java +++ b/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java @@ -23,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; @@ -90,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 { @@ -160,7 +159,7 @@ public void loadChapter(String chapterName, String scene, boolean initScene) thr else w.setCurrentScene(w.getScenes().get(scene), initScene); - 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()); @@ -439,7 +438,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()); } } diff --git a/blade-engine/src/com/bladecoder/engine/ui/DialogUI.java b/blade-engine/src/com/bladecoder/engine/ui/DialogUI.java index 2ddc63afc..a67d1e20c 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/DialogUI.java +++ b/blade-engine/src/com/bladecoder/engine/ui/DialogUI.java @@ -21,6 +21,7 @@ import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.ui.Button; import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane; +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.TextButton.TextButtonStyle; @@ -31,6 +32,7 @@ import com.badlogic.gdx.utils.Timer; import com.badlogic.gdx.utils.Timer.Task; import com.bladecoder.engine.i18n.I18N; +import com.bladecoder.engine.model.World; import com.bladecoder.engine.util.DPIUtils; import com.bladecoder.engine.util.EngineLogger; @@ -47,23 +49,21 @@ public class DialogUI extends ScrollPane { private Button down; private List choices; + private final World world; - private final UI ui; - - public DialogUI(UI ui) { - super(new Table(ui.getSkin()), ui.getSkin()); - - this.ui = ui; + public DialogUI(Skin skin, World w, Recorder recorder) { + super(new Table(skin), skin); setFadeScrollBars(true); setOverscroll(false, false); - up = new Button(ui.getSkin(), "dialog-up"); - down = new Button(ui.getSkin(), "dialog-down"); + up = new Button(skin, "dialog-up"); + down = new Button(skin, "dialog-down"); panel = (Table) getActor(); - style = ui.getSkin().get(DialogUIStyle.class); - this.recorder = ui.getRecorder(); + style = skin.get(DialogUIStyle.class); + this.recorder = recorder; + this.world = w; if (style.background != null) panel.setBackground(style.background); @@ -133,7 +133,7 @@ public void setScrollY(float pixels) { } private void show() { - choices = ui.getWorld().getDialogOptions(); + choices = world.getDialogOptions(); if (choices.size() == 0) { setVisible(false); @@ -161,7 +161,7 @@ public void run() { String str = choices.get(i); if (str.charAt(0) == I18N.PREFIX) - str = I18N.getString(str.substring(1)); + str = world.getI18N().getString(str.substring(1)); TextButton ob = new TextButton(str, style.textButtonStyle); ob.setUserObject(i); @@ -206,7 +206,7 @@ private void select(int i) { recorder.add(i); } - ui.getWorld().selectDialogOption(i); + world.selectDialogOption(i); setVisible(false); } diff --git a/blade-engine/src/com/bladecoder/engine/ui/HelpScreen.java b/blade-engine/src/com/bladecoder/engine/ui/HelpScreen.java index 89d0b75eb..f2003bc31 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/HelpScreen.java +++ b/blade-engine/src/com/bladecoder/engine/ui/HelpScreen.java @@ -31,7 +31,6 @@ import com.badlogic.gdx.utils.viewport.FitViewport; import com.badlogic.gdx.utils.viewport.Viewport; import com.bladecoder.engine.assets.EngineAssetManager; -import com.bladecoder.engine.i18n.I18N; import com.bladecoder.engine.ui.UI.Screens; import com.bladecoder.engine.ui.defaults.DefaultSceneScreen.UIModes; import com.bladecoder.engine.util.Config; @@ -51,9 +50,9 @@ public class HelpScreen extends ScreenAdapter implements BladeScreen { private String localeFilename; private final Viewport viewport; - + public HelpScreen() { - viewport= new FitViewport(Gdx.graphics.getWidth(), Gdx.graphics.getWidth() * 9f/16f); + viewport = new FitViewport(Gdx.graphics.getWidth(), Gdx.graphics.getWidth() * 9f / 16f); } private final InputProcessor inputProcessor = new InputAdapter() { @@ -93,21 +92,21 @@ public void render(float delta) { @Override public void resize(int width, int height) { - + float aspect = width / height; - float bgAspect = (float)tex.getWidth() / (float)tex.getHeight(); - - if(aspect < bgAspect) - viewport.setWorldSize(width, (int)(width / bgAspect)); + float bgAspect = (float) tex.getWidth() / (float) tex.getHeight(); + + if (aspect < bgAspect) + viewport.setWorldSize(width, (int) (width / bgAspect)); else - viewport.setWorldSize((int)(height * bgAspect), height); - + viewport.setWorldSize((int) (height * bgAspect), height); + viewport.update(width, height, true); } @Override public void dispose() { - if(tex != null) { + if (tex != null) { tex.dispose(); tex = null; } @@ -115,7 +114,7 @@ public void dispose() { @Override public void show() { - final Locale locale = I18N.getCurrentLocale(); + final Locale locale = ui.getWorld().getI18N().getCurrentLocale(); String filename = null; UIModes uiMode = UIModes.valueOf(Config.getProperty(Config.UI_MODE, "TWO_BUTTONS").toUpperCase(Locale.ENGLISH)); diff --git a/blade-engine/src/com/bladecoder/engine/ui/InventoryButton.java b/blade-engine/src/com/bladecoder/engine/ui/InventoryButton.java index 97924c3b4..39e7b5bf8 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/InventoryButton.java +++ b/blade-engine/src/com/bladecoder/engine/ui/InventoryButton.java @@ -2,21 +2,23 @@ import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.actions.Actions; +import com.badlogic.gdx.scenes.scene2d.ui.Skin; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; +import com.bladecoder.engine.model.World; import com.bladecoder.engine.ui.InventoryUI.InventoryPos; import com.bladecoder.engine.util.DPIUtils; public class InventoryButton extends AnimButton { private final InventoryUI inventory; - private final UI ui; + private final World world; private int numItems = Integer.MAX_VALUE; - public InventoryButton(UI ui, InventoryUI inv) { - super(ui.getSkin(), "inventory"); + public InventoryButton(Skin skin, World w, InventoryUI inv) { + super(skin, "inventory"); this.inventory = inv; - this.ui = ui; + this.world = w; addListener(new ChangeListener() { @Override @@ -33,13 +35,13 @@ public void changed(ChangeEvent event, Actor actor) { public void act(float delta) { super.act(delta); - if (numItems < ui.getWorld().getInventory().getNumItems()) { + if (numItems < world.getInventory().getNumItems()) { // addAction(Actions.repeat(30, Actions.sequence(Actions.rotateBy(40, 1f), Actions.rotateBy(-40, 1f)))); addAction(Actions.repeat(4, Actions.sequence(Actions.moveBy(20, 0, .06f), Actions.moveBy(-20, 0, .06f)))); // addAction(Actions.repeat(3, Actions.sequence(Actions.alpha(0, .4f), Actions.alpha(1, .4f)))); } - numItems = ui.getWorld().getInventory().getNumItems(); + numItems = world.getInventory().getNumItems(); } public void resize(int width, int height) { diff --git a/blade-engine/src/com/bladecoder/engine/ui/InventoryUI.java b/blade-engine/src/com/bladecoder/engine/ui/InventoryUI.java index 39b33a190..a65873331 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/InventoryUI.java +++ b/blade-engine/src/com/bladecoder/engine/ui/InventoryUI.java @@ -158,7 +158,7 @@ public void hide() { public void resize(int width, int height) { - Inventory inventory = sceneScreen.getUI().getWorld().getInventory(); + Inventory inventory = sceneScreen.getWorld().getInventory(); tileSize = (int) DPIUtils.getTouchMinSize() * 2; margin = (int) DPIUtils.getMarginSize(); @@ -242,7 +242,7 @@ public void retrieveAssets(TextureAtlas atlas) { @Override public void draw(Batch batch, float alpha) { - Inventory inventory = sceneScreen.getUI().getWorld().getInventory(); + Inventory inventory = sceneScreen.getWorld().getInventory(); if (!inventory.isVisible()) { setVisible(false); @@ -297,7 +297,7 @@ public boolean isDragging() { private final Vector3 mousepos = new Vector3(); private void stopDragging(int button) { - sceneScreen.getUI().getWorld().getSceneCamera().getInputUnProject(sceneScreen.getViewport(), mousepos); + sceneScreen.getWorld().getSceneCamera().getInputUnProject(sceneScreen.getViewport(), mousepos); InteractiveActor targetActor = sceneScreen.getCurrentActor(); @@ -340,7 +340,7 @@ public SpriteActor getItemAt(float x, float y) { if (x < margin || y < margin || x >= getWidth() - margin || y >= getHeight() - margin) return null; - Inventory inventory = sceneScreen.getUI().getWorld().getInventory(); + Inventory inventory = sceneScreen.getWorld().getInventory(); int i = ((rows - 1) - ((int) (y - margin) / (tileSize + (int) rowSpace))) * cols + (int) (x - margin) / (tileSize + (int) rowSpace); diff --git a/blade-engine/src/com/bladecoder/engine/ui/LoadSaveScreen.java b/blade-engine/src/com/bladecoder/engine/ui/LoadSaveScreen.java index e7976e768..db85eceff 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/LoadSaveScreen.java +++ b/blade-engine/src/com/bladecoder/engine/ui/LoadSaveScreen.java @@ -50,7 +50,6 @@ import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.viewport.ScreenViewport; import com.bladecoder.engine.assets.EngineAssetManager; -import com.bladecoder.engine.i18n.I18N; import com.bladecoder.engine.model.Text; import com.bladecoder.engine.model.TextManager; import com.bladecoder.engine.model.World; @@ -144,7 +143,9 @@ public void show() { table.center(); table.pad(pad); - Label title = new Label(loadScreenMode ? I18N.getString("ui.load") : I18N.getString("ui.save"), skin, "title"); + Label title = new Label( + loadScreenMode ? world.getI18N().getString("ui.load") : world.getI18N().getString("ui.save"), skin, + "title"); Button back = new Button(skin, "back"); @@ -228,7 +229,7 @@ public boolean keyUp(InputEvent event, int keycode) { table.row(); if (loadScreenMode && sl.size() == 0) { - Label lbl = new Label(I18N.getString("ui.noSavedGames"), skin, "title"); + Label lbl = new Label(world.getI18N().getString("ui.noSavedGames"), skin, "title"); lbl.setAlignment(Align.center); lbl.setWrap(true); table.add(lbl).expand().fill(); @@ -264,7 +265,7 @@ private Button getSlotButton(String slot) { final ButtonStyle style = button.getStyle(); style.up = style.down = skin.getDrawable("black"); - String textLabel = I18N.getString("ui.newSlot"); + String textLabel = ui.getWorld().getI18N().getString("ui.newSlot"); button.setSize(slotWidth, slotHeight); if (slotExists(slot)) { @@ -375,14 +376,16 @@ protected void result(Object object) { d.getButtonTable().padTop(DPIUtils.getMarginSize()); d.getButtonTable().defaults().padLeft(DPIUtils.getMarginSize()).padRight(DPIUtils.getMarginSize()); - Label l = new Label(I18N.getString("ui.override_load"), ui.getSkin(), "ui-dialog"); + Label l = new Label(world.getI18N().getString("ui.override_load"), ui.getSkin(), "ui-dialog"); l.setWrap(true); l.setAlignment(Align.center); d.getContentTable().add(l).prefWidth(Gdx.graphics.getWidth() * .7f); - d.button(I18N.getString("ui.yes"), true, ui.getSkin().get("ui-dialog", TextButtonStyle.class)); - d.button(I18N.getString("ui.no"), false, ui.getSkin().get("ui-dialog", TextButtonStyle.class)); + d.button(world.getI18N().getString("ui.yes"), true, + ui.getSkin().get("ui-dialog", TextButtonStyle.class)); + d.button(world.getI18N().getString("ui.no"), false, + ui.getSkin().get("ui-dialog", TextButtonStyle.class)); d.key(Keys.ENTER, true).key(Keys.ESCAPE, false); d.show(stage); @@ -430,14 +433,16 @@ protected void result(Object object) { d.getButtonTable().padTop(DPIUtils.getMarginSize()); d.getButtonTable().defaults().padLeft(DPIUtils.getMarginSize()).padRight(DPIUtils.getMarginSize()); - Label l = new Label(I18N.getString("ui.remove"), ui.getSkin(), "ui-dialog"); + Label l = new Label(ui.getWorld().getI18N().getString("ui.remove"), ui.getSkin(), "ui-dialog"); l.setWrap(true); l.setAlignment(Align.center); d.getContentTable().add(l).prefWidth(Gdx.graphics.getWidth() * .7f); - d.button(I18N.getString("ui.yes"), true, ui.getSkin().get("ui-dialog", TextButtonStyle.class)); - d.button(I18N.getString("ui.no"), false, ui.getSkin().get("ui-dialog", TextButtonStyle.class)); + d.button(ui.getWorld().getI18N().getString("ui.yes"), true, + ui.getSkin().get("ui-dialog", TextButtonStyle.class)); + d.button(ui.getWorld().getI18N().getString("ui.no"), false, + ui.getSkin().get("ui-dialog", TextButtonStyle.class)); d.key(Keys.ENTER, true).key(Keys.ESCAPE, false); d.show(stage); diff --git a/blade-engine/src/com/bladecoder/engine/ui/MenuScreen.java b/blade-engine/src/com/bladecoder/engine/ui/MenuScreen.java index dff1da2d4..24d74fec2 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/MenuScreen.java +++ b/blade-engine/src/com/bladecoder/engine/ui/MenuScreen.java @@ -43,7 +43,6 @@ import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.viewport.ScreenViewport; import com.bladecoder.engine.assets.EngineAssetManager; -import com.bladecoder.engine.i18n.I18N; import com.bladecoder.engine.model.World; import com.bladecoder.engine.ui.UI.Screens; import com.bladecoder.engine.util.Config; @@ -190,7 +189,8 @@ public boolean keyUp(InputEvent event, int keycode) { } if (world.savedGameExists() || world.getCurrentScene() != null) { - TextButton continueGame = new TextButton(I18N.getString("ui.continue"), skin, style.textButtonStyle); + TextButton continueGame = new TextButton(world.getI18N().getString("ui.continue"), skin, + style.textButtonStyle); continueGame.getLabel().setAlignment(getAlign()); continueGame.addListener(new ClickListener() { @@ -211,7 +211,7 @@ public void clicked(InputEvent event, float x, float y) { menuButtonTable.row(); } - TextButton newGame = new TextButton(I18N.getString("ui.new"), skin, style.textButtonStyle); + TextButton newGame = new TextButton(world.getI18N().getString("ui.new"), skin, style.textButtonStyle); newGame.getLabel().setAlignment(getAlign()); newGame.addListener(new ClickListener() { @Override @@ -236,14 +236,16 @@ protected void result(Object object) { d.getButtonTable().padTop(DPIUtils.getMarginSize()); d.getButtonTable().defaults().padLeft(DPIUtils.getMarginSize()).padRight(DPIUtils.getMarginSize()); - Label l = new Label(I18N.getString("ui.override"), ui.getSkin(), "ui-dialog"); + Label l = new Label(world.getI18N().getString("ui.override"), ui.getSkin(), "ui-dialog"); l.setWrap(true); l.setAlignment(Align.center); d.getContentTable().add(l).prefWidth(Gdx.graphics.getWidth() * .7f); - d.button(I18N.getString("ui.yes"), true, ui.getSkin().get("ui-dialog", TextButtonStyle.class)); - d.button(I18N.getString("ui.no"), false, ui.getSkin().get("ui-dialog", TextButtonStyle.class)); + d.button(world.getI18N().getString("ui.yes"), true, + ui.getSkin().get("ui-dialog", TextButtonStyle.class)); + d.button(world.getI18N().getString("ui.no"), false, + ui.getSkin().get("ui-dialog", TextButtonStyle.class)); d.key(Keys.ENTER, true).key(Keys.ESCAPE, false); d.show(stage); @@ -263,7 +265,7 @@ protected void result(Object object) { menuButtonTable.add(newGame); menuButtonTable.row(); - TextButton loadGame = new TextButton(I18N.getString("ui.load"), skin, style.textButtonStyle); + TextButton loadGame = new TextButton(world.getI18N().getString("ui.load"), skin, style.textButtonStyle); loadGame.getLabel().setAlignment(getAlign()); loadGame.addListener(new ClickListener() { @Override @@ -275,7 +277,7 @@ public void clicked(InputEvent event, float x, float y) { menuButtonTable.add(loadGame); menuButtonTable.row(); - TextButton quit = new TextButton(I18N.getString("ui.quit"), skin, style.textButtonStyle); + TextButton quit = new TextButton(world.getI18N().getString("ui.quit"), skin, style.textButtonStyle); quit.getLabel().setAlignment(getAlign()); quit.addListener(new ClickListener() { @Override diff --git a/blade-engine/src/com/bladecoder/engine/ui/PieMenu.java b/blade-engine/src/com/bladecoder/engine/ui/PieMenu.java index 05556d7ad..1293f3275 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/PieMenu.java +++ b/blade-engine/src/com/bladecoder/engine/ui/PieMenu.java @@ -138,7 +138,7 @@ public void show(InteractiveActor a, float x, float y) { if (desc != null) { if (desc.charAt(0) == I18N.PREFIX) - desc = I18N.getString(desc.substring(1)); + desc = sceneScreen.getWorld().getI18N().getString(desc.substring(1)); layout.setText(font, desc); } diff --git a/blade-engine/src/com/bladecoder/engine/ui/PieMenu2.java b/blade-engine/src/com/bladecoder/engine/ui/PieMenu2.java index b54529f37..5d033ac7f 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/PieMenu2.java +++ b/blade-engine/src/com/bladecoder/engine/ui/PieMenu2.java @@ -30,7 +30,7 @@ import com.bladecoder.engine.util.RectangleRenderer; public class PieMenu2 extends com.badlogic.gdx.scenes.scene2d.Group { - + private final static int NUM_VERBS = 10; private BitmapFont font; @@ -39,18 +39,18 @@ public class PieMenu2 extends com.badlogic.gdx.scenes.scene2d.Group { private Vector2[] endPositions; private float x = 0, y = 0; - + private float distance = 100; - private float minAngle = 0, maxAngle = 360, startAngle = 0; + private float minAngle = 0, maxAngle = 360, startAngle = 0; private InteractiveActor iActor = null; private final SceneScreen sceneScreen; - + private int viewportWidth, viewportHeight; - + private final GlyphLayout layout = new GlyphLayout(); - + private String desc = null; public PieMenu2(SceneScreen scr) { @@ -58,13 +58,13 @@ public PieMenu2(SceneScreen scr) { font = scr.getUI().getSkin().getFont("desc"); buttons = new Button[NUM_VERBS]; endPositions = new Vector2[NUM_VERBS]; - - for(int i = 0; i < NUM_VERBS; i++) { - buttons[i] = new Button(scr.getUI().getSkin(), "pie_lookat"); + + for (int i = 0; i < NUM_VERBS; i++) { + buttons[i] = new Button(scr.getUI().getSkin(), "pie_lookat"); endPositions[i] = new Vector2(); addActor(buttons[i]); - - buttons[i].addListener(new ChangeListener() { + + buttons[i].addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, com.badlogic.gdx.scenes.scene2d.Actor actor) { if (iActor != null) { @@ -85,7 +85,7 @@ public void draw(Batch batch, float alpha) { // DRAW TARGET DESCRIPTION String desc = iActor.getDesc(); - if (desc != null) { + if (desc != null) { float margin = DPIUtils.UI_SPACE; float textX = x - layout.width / 2; @@ -94,8 +94,8 @@ public void draw(Batch batch, float alpha) { if (textX < 0) textX = 0; - RectangleRenderer.draw(batch, textX - margin, textY - layout.height - margin, - layout.width + margin*2, layout.height + margin*2, Color.BLACK); + RectangleRenderer.draw(batch, textX - margin, textY - layout.height - margin, layout.width + margin * 2, + layout.height + margin * 2, Color.BLACK); font.draw(batch, layout, textX, textY); } } @@ -110,43 +110,43 @@ public void show(InteractiveActor a, float x, float y) { this.x = x; this.y = y; iActor = a; - + // DRAW TARGET DESCRIPTION desc = iActor.getDesc(); if (desc != null) { if (desc.charAt(0) == I18N.PREFIX) - desc = I18N.getString(desc.substring(1)); - + desc = sceneScreen.getWorld().getI18N().getString(desc.substring(1)); + layout.setText(font, desc); } - + float margin = DPIUtils.getMarginSize(); - + // FITS TO SCREEN - if(x < distance + buttons[0].getWidth() / 2 + margin) + if (x < distance + buttons[0].getWidth() / 2 + margin) this.x = distance + buttons[0].getWidth() / 2 + margin; - else if(x > viewportWidth - distance - buttons[0].getWidth() / 2 - margin) + else if (x > viewportWidth - distance - buttons[0].getWidth() / 2 - margin) this.x = viewportWidth - distance - buttons[0].getWidth() / 2 - margin; - - if(y < distance + buttons[0].getHeight() / 2 + margin) + + if (y < distance + buttons[0].getHeight() / 2 + margin) this.y = distance + buttons[0].getHeight() / 2 + margin; - else if(y > viewportHeight - distance - buttons[0].getHeight() / 2 - margin) + else if (y > viewportHeight - distance - buttons[0].getHeight() / 2 - margin) this.y = viewportHeight - distance - buttons[0].getHeight() / 2 - margin; - - float offsetAngle = ((maxAngle - minAngle)) / (NUM_VERBS - 1); - float angle = startAngle; - - for(int i = 0; i < NUM_VERBS; i++) { - - endPositions[i].x = (float)(Math.cos(Math.toRadians(angle))) * distance + this.x; - endPositions[i].y = (float)(Math.sin(Math.toRadians(angle))) * distance + this.y; - + + float offsetAngle = ((maxAngle - minAngle)) / (NUM_VERBS - 1); + float angle = startAngle; + + for (int i = 0; i < NUM_VERBS; i++) { + + endPositions[i].x = (float) (Math.cos(Math.toRadians(angle))) * distance + this.x; + endPositions[i].y = (float) (Math.sin(Math.toRadians(angle))) * distance + this.y; + buttons[i].setPosition(this.x - buttons[i].getWidth() / 2, this.y - buttons[i].getHeight() / 2); - buttons[i].addAction(Actions - .moveTo(endPositions[i].x - buttons[i].getWidth() / 2, endPositions[i].y - buttons[i].getWidth() / 2, .1f)); - + buttons[i].addAction(Actions.moveTo(endPositions[i].x - buttons[i].getWidth() / 2, + endPositions[i].y - buttons[i].getWidth() / 2, .1f)); + angle += offsetAngle; } @@ -155,10 +155,10 @@ else if(y > viewportHeight - distance - buttons[0].getHeight() / 2 - margin) public void resize(int width, int height) { viewportWidth = width; viewportHeight = height; - + setBounds(0, 0, width, height); } - + /** * The style for the PieMenu2. * diff --git a/blade-engine/src/com/bladecoder/engine/ui/SceneScreen.java b/blade-engine/src/com/bladecoder/engine/ui/SceneScreen.java index f022ba466..0f48246e3 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/SceneScreen.java +++ b/blade-engine/src/com/bladecoder/engine/ui/SceneScreen.java @@ -17,17 +17,22 @@ import com.badlogic.gdx.utils.viewport.Viewport; import com.bladecoder.engine.model.InteractiveActor; +import com.bladecoder.engine.model.World; public interface SceneScreen extends BladeScreen { UI getUI(); + World getWorld(); + Viewport getViewport(); InteractiveActor getCurrentActor(); void actorClick(InteractiveActor actor, int button); + void runVerb(InteractiveActor a, String verb, String target); float getSpeed(); + void setSpeed(float speed); } diff --git a/blade-engine/src/com/bladecoder/engine/ui/TextManagerUI.java b/blade-engine/src/com/bladecoder/engine/ui/TextManagerUI.java index 64fe7877c..716cc78ef 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/TextManagerUI.java +++ b/blade-engine/src/com/bladecoder/engine/ui/TextManagerUI.java @@ -23,6 +23,7 @@ import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.Touchable; +import com.badlogic.gdx.scenes.scene2d.ui.Skin; import com.badlogic.gdx.scenes.scene2d.utils.Drawable; import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.ObjectMap; @@ -30,6 +31,7 @@ import com.bladecoder.engine.model.CharacterActor; import com.bladecoder.engine.model.Text; import com.bladecoder.engine.model.TextManager; +import com.bladecoder.engine.model.World; import com.bladecoder.engine.util.Config; import com.bladecoder.engine.util.DPIUtils; import com.bladecoder.engine.util.EngineLogger; @@ -58,12 +60,12 @@ public class TextManagerUI extends Actor implements ITextManagerUI { private TextManagerUIStyle style; private float maxWidth; - private final UI ui; + private final World world; - public TextManagerUI(UI ui) { - this.ui = ui; + public TextManagerUI(Skin skin, World w) { + this.world = w; setTouchable(Touchable.disabled); - styles = ui.getSkin().getAll(TextManagerUIStyle.class); + styles = skin.getAll(TextManagerUIStyle.class); for (TextManagerUIStyle style : styles.values()) { style.font.getData().markupEnabled = true; @@ -105,7 +107,7 @@ private void calcPos() { float posy = text.y; unprojectTmp.set(posx, posy, 0); - ui.getWorld().getSceneCamera().scene2screen(getStage().getViewport(), unprojectTmp); + world.getSceneCamera().scene2screen(getStage().getViewport(), unprojectTmp); if (posx == TextManager.POS_CENTER || posx == TextManager.POS_SUBTITLE) { posx = getStage().getViewport().getScreenWidth() / 2; @@ -138,7 +140,7 @@ private void calcPos() { text.actorId); if (charIcon != null) { - float scale = getStage().getViewport().getScreenHeight() / (float) ui.getWorld().getHeight(); + float scale = getStage().getViewport().getScreenHeight() / (float) world.getHeight(); float iconPosY = getStage().getViewport().getScreenHeight() - charIcon.getRegionHeight() * scale - DPIUtils.getMarginSize(); posy = Math.min(posy, iconPosY); @@ -211,7 +213,7 @@ public void draw(Batch batch, float alpha) { } if (charIcon != null) { - float scale = getStage().getViewport().getScreenHeight() / (float) ui.getWorld().getHeight(); + float scale = getStage().getViewport().getScreenHeight() / (float) world.getHeight(); batch.draw(charIcon, getX() - charIcon.getRegionWidth() * scale, getY(), charIcon.getRegionWidth() * scale, charIcon.getRegionHeight() * scale); } @@ -227,7 +229,7 @@ private TextManagerUIStyle getStyle(Text text) { if (text != null && text.style != null && !text.style.isEmpty()) { key = text.style; } else if (text.actorId != null) { - CharacterActor a = (CharacterActor) ui.getWorld().getCurrentScene().getActor(text.actorId, false); + CharacterActor a = (CharacterActor) world.getCurrentScene().getActor(text.actorId, false); if (a != null && a.getTextStyle() != null) key = a.getTextStyle(); diff --git a/blade-engine/src/com/bladecoder/engine/ui/UI.java b/blade-engine/src/com/bladecoder/engine/ui/UI.java index cfa60ebe4..46a5f7e40 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/UI.java +++ b/blade-engine/src/com/bladecoder/engine/ui/UI.java @@ -17,6 +17,7 @@ import com.badlogic.gdx.Application.ApplicationType; import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Input; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.TextureAtlas; @@ -47,7 +48,8 @@ public class UI { private final World w; public enum Screens { - INIT_SCREEN, SCENE_SCREEN, LOADING_SCREEN, MENU_SCREEN, HELP_SCREEN, CREDIT_SCREEN, LOAD_GAME_SCREEN, SAVE_GAME_SCREEN + INIT_SCREEN, SCENE_SCREEN, LOADING_SCREEN, MENU_SCREEN, HELP_SCREEN, CREDIT_SCREEN, LOAD_GAME_SCREEN, + SAVE_GAME_SCREEN } private final BladeScreen screens[]; @@ -61,7 +63,8 @@ public UI(World w) { screens = new BladeScreen[Screens.values().length]; - Gdx.input.setCatchMenuKey(true); + Gdx.input.setCatchKey(Input.Keys.MENU, true); + Gdx.input.setCatchKey(Input.Keys.BACK, true); loadAssets(); diff --git a/blade-engine/src/com/bladecoder/engine/ui/defaults/DefaultSceneScreen.java b/blade-engine/src/com/bladecoder/engine/ui/defaults/DefaultSceneScreen.java index 379efb5f0..58f488abf 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/defaults/DefaultSceneScreen.java +++ b/blade-engine/src/com/bladecoder/engine/ui/defaults/DefaultSceneScreen.java @@ -150,6 +150,11 @@ public UI getUI() { return ui; } + @Override + public World getWorld() { + return ui.getWorld(); + } + public boolean isUiEnabled() { return uiEnabled; } @@ -194,7 +199,7 @@ void showUIText(Text t) { stage.addActor(msg); unprojectTmp.set(t.x, t.y, 0); - ui.getWorld().getSceneCamera().scene2screen(getStage().getViewport(), unprojectTmp); + getWorld().getSceneCamera().scene2screen(getStage().getViewport(), unprojectTmp); float posx, posy; @@ -221,7 +226,7 @@ void showUIText(Text t) { } void updateUI() { - World w = ui.getWorld(); + World w = getWorld(); if (w.getAssetState() == null || w.getAssetState() == AssetState.LOAD_ASSETS || w.getAssetState() == AssetState.LOAD_ASSETS_AND_INIT_SCENE) @@ -244,7 +249,7 @@ void updateUI() { EngineLogger.debug("Updating UI: DISABLED."); } else { - if (ui.getWorld().hasDialogOptions()) { + if (getWorld().hasDialogOptions()) { inventoryUI.hide(); inventoryButton.setVisible(false); dialogUI.setVisible(true); @@ -277,7 +282,7 @@ public float getSpeed() { } private void update(float delta) { - final World world = ui.getWorld(); + final World world = getWorld(); float deltaScaled = delta * speed; if (!world.isDisposed()) { @@ -402,7 +407,7 @@ else if (Gdx.input.isPeripheralAvailable(Peripheral.MultitouchScreen)) else tolerance = 0; - return ui.getWorld().getInteractiveActorAtInput(viewport, tolerance); + return getWorld().getInteractiveActorAtInput(viewport, tolerance); } /** @@ -449,7 +454,7 @@ private float calcLeaveArrowRotation(InteractiveActor actor) { @Override public void render(float delta) { - final World world = ui.getWorld(); + final World world = getWorld(); update(delta); @@ -502,7 +507,7 @@ public void render(float delta) { } private void drawDebugText(SpriteBatch batch) { - World w = ui.getWorld(); + World w = getWorld(); w.getSceneCamera().getInputUnProject(viewport, unprojectTmp); @@ -591,7 +596,7 @@ private void drawDebugText(SpriteBatch batch) { } private void drawHotspots(SpriteBatch batch) { - final World world = ui.getWorld(); + final World world = getWorld(); for (BaseActor a : world.getCurrentScene().getActors().values()) { if (!(a instanceof InteractiveActor) || !a.isVisible() || a == world.getCurrentScene().getPlayer()) continue; @@ -631,7 +636,7 @@ private void drawHotspots(SpriteBatch batch) { BitmapFont font = getUI().getSkin().getFont("desc"); String desc = ia.getDesc(); if (desc.charAt(0) == I18N.PREFIX) - desc = I18N.getString(desc.substring(1)); + desc = getWorld().getI18N().getString(desc.substring(1)); textLayout.setText(font, desc); @@ -647,7 +652,7 @@ private void drawHotspots(SpriteBatch batch) { @Override public void resize(int width, int height) { - final World world = ui.getWorld(); + final World world = getWorld(); if (!world.isDisposed()) { viewport.setWorldSize(world.getWidth(), world.getHeight()); viewport.update(width, height, true); @@ -684,7 +689,7 @@ private void retrieveAssets(TextureAtlas atlas) { } void sceneClick(int button, int count) { - World w = ui.getWorld(); + World w = getWorld(); w.getSceneCamera().getInputUnProject(viewport, unprojectTmp); @@ -837,9 +842,9 @@ public void show() { multiplexer.addProcessor(inputProcessor); Gdx.input.setInputProcessor(multiplexer); - if (ui.getWorld().isDisposed()) { + if (getWorld().isDisposed()) { try { - ui.getWorld().load(); + getWorld().load(); } catch (Exception e) { EngineLogger.error("ERROR LOADING GAME", e); @@ -848,23 +853,23 @@ public void show() { } } - ui.getWorld().setListener(worldListener); - ui.getWorld().resume(); + getWorld().setListener(worldListener); + getWorld().resume(); } @Override public void hide() { - ui.getWorld().pause(); + getWorld().pause(); } @Override public void pause() { - ui.getWorld().pause(); + getWorld().pause(); } @Override public void resume() { - ui.getWorld().resume(); + getWorld().resume(); // resets the error when continue if (EngineLogger.lastError != null && EngineLogger.debugMode()) { @@ -891,12 +896,12 @@ public void setUI(final UI ui) { testerBot = ui.getTesterBot(); pie = new PieMenu(this); - textManagerUI = new TextManagerUI(ui); + textManagerUI = new TextManagerUI(ui.getSkin(), getWorld()); menuButton = new AnimButton(ui.getSkin(), "menu"); - dialogUI = new DialogUI(ui); - pointer = new ScenePointer(ui.getSkin()); + dialogUI = new DialogUI(ui.getSkin(), getWorld(), ui.getRecorder()); + pointer = new ScenePointer(ui.getSkin(), getWorld()); inventoryUI = new InventoryUI(this, pointer); - inventoryButton = new InventoryButton(ui, inventoryUI); + inventoryButton = new InventoryButton(ui.getSkin(), getWorld(), inventoryUI); uiMode = UIModes.valueOf(Config.getProperty(Config.UI_MODE, "TWO_BUTTONS").toUpperCase(Locale.ENGLISH)); diff --git a/blade-engine/src/com/bladecoder/engine/ui/defaults/ScenePointer.java b/blade-engine/src/com/bladecoder/engine/ui/defaults/ScenePointer.java index 91dd286d9..910677845 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/defaults/ScenePointer.java +++ b/blade-engine/src/com/bladecoder/engine/ui/defaults/ScenePointer.java @@ -29,6 +29,7 @@ import com.bladecoder.engine.i18n.I18N; import com.bladecoder.engine.model.ActorRenderer; import com.bladecoder.engine.model.SpriteActor; +import com.bladecoder.engine.model.World; import com.bladecoder.engine.ui.AnimationDrawable; import com.bladecoder.engine.util.DPIUtils; import com.bladecoder.engine.util.RectangleRenderer; @@ -56,12 +57,12 @@ public class ScenePointer { private float pointerScale; private float leaveRotation = 0f; - // private Skin skin; + private final World world; private final GlyphLayout layout = new GlyphLayout(); - public ScenePointer(Skin skin) { - // this.skin = skin; + public ScenePointer(Skin skin, World w) { + this.world = w; font = skin.getFont("desc"); pointerIcon = skin.getDrawable(POINTER_ICON); leaveIcon = skin.getDrawable(LEAVE_ICON); @@ -106,7 +107,7 @@ public void setDesc(String s) { if (desc != null) { if (desc.charAt(0) == I18N.PREFIX) - desc = I18N.getString(desc.substring(1)); + desc = world.getI18N().getString(desc.substring(1)); layout.setText(font, desc); } diff --git a/blade-engine/src/com/bladecoder/engine/ui/retro/RetroSceneScreen.java b/blade-engine/src/com/bladecoder/engine/ui/retro/RetroSceneScreen.java index 6603435c7..c027d02bb 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/retro/RetroSceneScreen.java +++ b/blade-engine/src/com/bladecoder/engine/ui/retro/RetroSceneScreen.java @@ -327,8 +327,7 @@ private void updateUI() { /** * Sets the game speed. Can be used to fastfordward * - * @param s - * The multiplier speed. ej. 2.0 + * @param s The multiplier speed. ej. 2.0 */ @Override public void setSpeed(float s) { @@ -550,7 +549,7 @@ private void drawHotspots(SpriteBatch batch) { BitmapFont font = getUI().getSkin().getFont("desc"); String desc = ia.getDesc(); if (desc.charAt(0) == I18N.PREFIX) - desc = I18N.getString(desc.substring(1)); + desc = getWorld().getI18N().getString(desc.substring(1)); textLayout.setText(font, desc); @@ -731,12 +730,17 @@ public void setUI(UI ui) { recorder = ui.getRecorder(); testerBot = ui.getTesterBot(); - textManagerUI = new TextManagerUI(ui); + textManagerUI = new TextManagerUI(ui.getSkin(), getWorld()); menuButton = new Button(ui.getSkin(), "menu"); - dialogUI = new DialogUI(ui); + dialogUI = new DialogUI(ui.getSkin(), getWorld(), recorder); verbUI = new VerbUI(this); pointer = new Pointer(ui.getSkin()); } + + @Override + public World getWorld() { + return ui.getWorld(); + } } diff --git a/blade-engine/src/com/bladecoder/engine/ui/retro/VerbUI.java b/blade-engine/src/com/bladecoder/engine/ui/retro/VerbUI.java index 6dcf1f0ef..47eadd1c5 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/retro/VerbUI.java +++ b/blade-engine/src/com/bladecoder/engine/ui/retro/VerbUI.java @@ -24,7 +24,7 @@ public class VerbUI extends Table { private final static float MARGIN = 1; - + private static final List VERBS = Arrays.asList("give", "pickup", "use", "open", "talkto", "push", "close", "lookat", "pull"); private static final List VERBS_DESC = Arrays.asList("Give", "Pick up", "Use", "Open", "Talk to", "Push", @@ -35,7 +35,7 @@ public class VerbUI extends Table { private static final int INVENTORY_COLS = 3; private static final int INVENTORY_ROWS = 3; - private final List inventorySlots = new ArrayList(); + private final List inventorySlots = new ArrayList<>(); private final SceneScreen sceneScreen; private final String DEFAULT_VERB = "lookat"; @@ -45,9 +45,9 @@ public class VerbUI extends Table { private InteractiveActor target; private VerbUIStyle style; - + private int scroll = 0; - + private Table arrowPanel; private Table invPanel; @@ -68,19 +68,19 @@ public VerbUI(SceneScreen scn) { Table verbs = createVerbPanel(); add(verbs).fill().expand(); - + arrowPanel = createArrowPanel(); add(arrowPanel).fillY().expandY(); invPanel = createInventoryPanel(); add(invPanel).fill().expand(); } - + private Table createArrowPanel() { Table arrows = new Table(); - + arrows.defaults().pad(MARGIN); - + ImageButton.ImageButtonStyle s = new ImageButton.ImageButtonStyle(style.inventoryButtonStyle); s.imageUp = style.upArrow; @@ -89,14 +89,15 @@ private Table createArrowPanel() { arrows.add(up).fillY().expandY(); up.addListener(new ClickListener() { + @Override public void clicked(InputEvent event, float x, float y) { - if(scroll > 0) + if (scroll > 0) scroll--; } }); - + arrows.row(); - + ImageButton.ImageButtonStyle s2 = new ImageButton.ImageButtonStyle(style.inventoryButtonStyle); s2.imageUp = style.downArrow; @@ -105,22 +106,23 @@ public void clicked(InputEvent event, float x, float y) { arrows.add(down).fillY().expandY(); down.addListener(new ClickListener() { + @Override public void clicked(InputEvent event, float x, float y) { Inventory inv = sceneScreen.getUI().getWorld().getInventory(); - + int itemsLeft = inv.getNumItems() - scroll * INVENTORY_COLS; - - if(itemsLeft > inventorySlots.size()) + + if (itemsLeft > inventorySlots.size()) scroll++; } }); - + return arrows; } private Table createVerbPanel() { Table verbs = new Table(); - + verbs.defaults().pad(MARGIN); for (int i = 0; i < VERBS.size(); i++) { @@ -130,6 +132,7 @@ private Table createVerbPanel() { TextButton b = new TextButton(VERBS_DESC.get(i), style.verbButtonStyle); b.setName(VERBS.get(i)); b.addListener(new ClickListener() { + @Override public void clicked(InputEvent event, float x, float y) { currentVerb = event.getListenerActor().getName(); infoLine.setText(((TextButton) event.getListenerActor()).getText()); @@ -142,16 +145,16 @@ public void clicked(InputEvent event, float x, float y) { return verbs; } - + @Override public void sizeChanged() { super.sizeChanged(); - for(Actor a:arrowPanel.getChildren()) { - ImageButton b = (ImageButton)a; - float h = (getHeight() / 2) - style.infoLineLabelStyle.font.getLineHeight() / 2 - DPIUtils.getSpacing(); + for (Actor a : arrowPanel.getChildren()) { + ImageButton b = (ImageButton) a; + float h = (getHeight() / 2) - style.infoLineLabelStyle.font.getLineHeight() / 2 - DPIUtils.getSpacing(); float ih = b.getImage().getDrawable().getMinHeight(); - float iw = b.getImage().getDrawable().getMinWidth() * h / ih; + float iw = b.getImage().getDrawable().getMinWidth() * h / ih; b.getImageCell().maxSize(iw, h); } @@ -167,22 +170,22 @@ public void act(float delta) { // fill inventory for (int i = 0; i < inventorySlots.size(); i++) { RendererDrawable r = (RendererDrawable) inventorySlots.get(i).getImage().getDrawable(); - + int pos = scroll * INVENTORY_COLS + i; if (pos < inv.getNumItems()) { - r.setRenderer(inv.get(pos).getRenderer()); + r.setRenderer(inv.get(pos).getRenderer()); } else { r.setRenderer(null); } - + inventorySlots.get(i).getImage().invalidate(); } } private Table createInventoryPanel() { Table inventory = new Table(); - + inventory.defaults().pad(MARGIN); for (int i = 0; i < INVENTORY_COLS * INVENTORY_ROWS; i++) { @@ -200,6 +203,7 @@ private Table createInventoryPanel() { inventorySlots.add(b); b.addListener(new ClickListener() { + @Override public void clicked(InputEvent event, float x, float y) { int i = (Integer) event.getListenerActor().getUserObject(); Inventory inv = sceneScreen.getUI().getWorld().getInventory(); @@ -216,7 +220,7 @@ public void clicked(InputEvent event, float x, float y) { } } }); - + b.getImageCell().pad(MARGIN).expand().fill(); } @@ -263,7 +267,7 @@ private String getTranslatedDesc(InteractiveActor actor) { desc = actor.getDesc(); if (desc.charAt(0) == I18N.PREFIX) - desc = I18N.getString(desc.substring(1)); + desc = sceneScreen.getWorld().getI18N().getString(desc.substring(1)); } return desc; From ae4135fea2cc3795b1afb3acb6ea65f96a4e1e33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Garc=C3=ADa?= Date: Thu, 21 Nov 2019 11:37:20 +0100 Subject: [PATCH 060/147] Check if the walkzone is set before add the obstacle to the walkzone. --- .../engine/polygonalpathfinder/PolygonalNavGraph.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/blade-engine/src/com/bladecoder/engine/polygonalpathfinder/PolygonalNavGraph.java b/blade-engine/src/com/bladecoder/engine/polygonalpathfinder/PolygonalNavGraph.java index 40588a7c4..dfc2aa112 100644 --- a/blade-engine/src/com/bladecoder/engine/polygonalpathfinder/PolygonalNavGraph.java +++ b/blade-engine/src/com/bladecoder/engine/polygonalpathfinder/PolygonalNavGraph.java @@ -42,14 +42,14 @@ 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 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) { final NavPathPolygonal resultPath = new NavPathPolygonal(); @@ -283,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); } From ddcbb6272a0417d5c0120894df6c9b4327a5413b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Garc=C3=ADa?= Date: Thu, 21 Nov 2019 18:49:16 +0100 Subject: [PATCH 061/147] Use method to get the world instance. --- .../bladecoder/engine/ui/defaults/SceneGestureListener.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blade-engine/src/com/bladecoder/engine/ui/defaults/SceneGestureListener.java b/blade-engine/src/com/bladecoder/engine/ui/defaults/SceneGestureListener.java index 6a23f0425..c808db6e8 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/defaults/SceneGestureListener.java +++ b/blade-engine/src/com/bladecoder/engine/ui/defaults/SceneGestureListener.java @@ -38,7 +38,7 @@ public boolean touchDown(float x, float y, int pointer, int button) { public boolean tap(float x, float y, int count, int button) { EngineLogger.debug("Event TAP button: " + button + " count: " + count); - World w = dsc.getUI().getWorld(); + World w = dsc.getWorld(); if (w.getAssetState() != AssetState.LOADED || w.isPaused() || dsc.getUI().getRecorder().isPlaying() || dsc.getUI().getTesterBot().isEnabled()) @@ -78,7 +78,7 @@ public boolean tap(float x, float y, int count, int button) { public boolean longPress(float x, float y) { EngineLogger.debug("Event LONG PRESS"); - if (dsc.isUiEnabled() && !dsc.getUI().getWorld().hasDialogOptions()) { + if (dsc.isUiEnabled() && !dsc.getWorld().hasDialogOptions()) { dsc.setDrawHotspots(true); } From d77caf37a6fd1abe291172bbed089b60d34bcd28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Garc=C3=ADa?= Date: Fri, 22 Nov 2019 15:14:55 +0100 Subject: [PATCH 062/147] Better handling of cancelled ink VerbRunner. --- .../com/bladecoder/engine/ink/InkManager.java | 198 ++++++++++-------- .../ActionCallbackSerializer.java | 23 +- 2 files changed, 119 insertions(+), 102 deletions(-) diff --git a/blade-engine/src/com/bladecoder/engine/ink/InkManager.java b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java index c60f0d9f2..6b6df4419 100644 --- a/blade-engine/src/com/bladecoder/engine/ink/InkManager.java +++ b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java @@ -33,7 +33,7 @@ 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 = '='; @@ -51,22 +51,19 @@ 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 { @@ -190,9 +187,11 @@ private void continueMaximally() { waitIfNotLoaded(); String line = null; - // Cancel possible pending timer - w.getCurrentScene().getTimers().removeTimerWithCb(this); - 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<>(); @@ -230,8 +229,8 @@ private void continueMaximally() { } - if (actions.size() > 0) { - run(null, null); + if (inkVerbRunner.getActions().size() > 0) { + inkVerbRunner.run(null, null); } else { if (hasChoices()) { @@ -321,7 +320,7 @@ private void processCommand(HashMap params, String line) { action = ActionFactory.create(commandName, params); action.init(w); - actions.add(action); + inkVerbRunner.getActions().add(action); } catch (ClassNotFoundException | ReflectionException e) { EngineLogger.error(e.getMessage(), e); } @@ -369,37 +368,12 @@ private void processTextLine(HashMap params, String line) { } 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() { return story; } @@ -421,7 +395,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() { @@ -471,12 +445,6 @@ private String getJsonString(InputStream is) throws IOException { } } - @Override - public void resume() { - ip++; - nextStep(); - } - public void selectChoice(int i) { w.setCutMode(wasInCutmode); @@ -488,47 +456,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() { - // Cancel possible pending timer - w.getCurrentScene().getTimers().removeTimerWithCb(this); - - 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; } @@ -563,6 +490,10 @@ public void run() { loaderThread.start(); } + public InkVerbRunner getVerbRunner() { + return inkVerbRunner; + } + @Override public void write(Json json) { BladeJson bjson = (BladeJson) json; @@ -581,15 +512,15 @@ public void write(Json json) { // 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); @@ -631,22 +562,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; @@ -663,4 +596,83 @@ 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: " + a.getClass().getSimpleName(), e); + ip++; + } + } + + if (ip >= actions.size() && !stop) + continueMaximally(); + } + } + + } } diff --git a/blade-engine/src/com/bladecoder/engine/serialization/ActionCallbackSerializer.java b/blade-engine/src/com/bladecoder/engine/serialization/ActionCallbackSerializer.java index 407806423..5fd925a2e 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,8 +181,7 @@ 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) { @@ -198,7 +203,7 @@ public static String find(World w, ActionCallback cb) { return id; // search in inkManager actions - id = find(cb, w.getInkManager()); + id = find(cb, w.getInkManager().getVerbRunner()); if (id != null) return id; @@ -256,10 +261,10 @@ public static ActionCallback find(World w, String id) { 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; From 3c9a28cb12cdbe0895c7281b1a06cfe3d25cfb13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Garc=C3=ADa?= Date: Wed, 27 Nov 2019 19:51:00 +0100 Subject: [PATCH 063/147] Update blade-ink to v0.7.3 --- adventure-editor/src/main/resources/versions.properties | 2 +- blade-engine/src/com/bladecoder/engine/ink/InkManager.java | 2 +- gradle.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/adventure-editor/src/main/resources/versions.properties b/adventure-editor/src/main/resources/versions.properties index a2339cbe3..85bfd59bc 100644 --- a/adventure-editor/src/main/resources/versions.properties +++ b/adventure-editor/src/main/resources/versions.properties @@ -1,6 +1,6 @@ androidAPILevel=28 androidGradlePluginVersion=3.4.1 -bladeInkVersion=0.7.2 +bladeInkVersion=0.7.3 buildToolsVersion=28.0.3 libgdxVersion=1.9.10 roboVMGradlePluginVersion=2.3.7 diff --git a/blade-engine/src/com/bladecoder/engine/ink/InkManager.java b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java index 6b6df4419..ba6f377b7 100644 --- a/blade-engine/src/com/bladecoder/engine/ink/InkManager.java +++ b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java @@ -154,7 +154,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); } } diff --git a/gradle.properties b/gradle.properties index 409c9c1d2..963c43dbd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ roboVMGradlePluginVersion=2.3.7 buildToolsVersion=28.0.3 androidAPILevel=28 androidGradlePluginVersion=3.4.1 -bladeInkVersion=0.7.2 +bladeInkVersion=0.7.3 From 5a986ee7b0808d8a42ce983b9aa03e753c7372c4 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sun, 1 Dec 2019 12:10:30 +0100 Subject: [PATCH 064/147] Inventory button style now in the inventoryui style to allow custom style by player. --- .../src/com/bladecoder/engine/ui/AnimButton.java | 4 ++++ .../com/bladecoder/engine/ui/InventoryButton.java | 14 +++++++++++++- .../src/com/bladecoder/engine/ui/InventoryUI.java | 6 ++++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/blade-engine/src/com/bladecoder/engine/ui/AnimButton.java b/blade-engine/src/com/bladecoder/engine/ui/AnimButton.java index 0dd4b1d47..fa0f36986 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/AnimButton.java +++ b/blade-engine/src/com/bladecoder/engine/ui/AnimButton.java @@ -8,6 +8,10 @@ public AnimButton(Skin skin, String styleName) { super(skin, styleName); } + public AnimButton(ButtonStyle style) { + super(style); + } + @Override public void act(float delta) { ButtonStyle style = getStyle(); diff --git a/blade-engine/src/com/bladecoder/engine/ui/InventoryButton.java b/blade-engine/src/com/bladecoder/engine/ui/InventoryButton.java index 39e7b5bf8..26dcc1f7f 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/InventoryButton.java +++ b/blade-engine/src/com/bladecoder/engine/ui/InventoryButton.java @@ -6,6 +6,7 @@ import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.bladecoder.engine.model.World; import com.bladecoder.engine.ui.InventoryUI.InventoryPos; +import com.bladecoder.engine.ui.InventoryUI.InventoryUIStyle; import com.bladecoder.engine.util.DPIUtils; public class InventoryButton extends AnimButton { @@ -15,8 +16,19 @@ public class InventoryButton extends AnimButton { private int numItems = Integer.MAX_VALUE; + private static ButtonStyle getDefaultStyle(Skin skin) { + InventoryUIStyle inventoryUIStyle = skin.get(InventoryUIStyle.class); + ButtonStyle inventoryButtonStyle = inventoryUIStyle.inventoryButtonStyle; + + if (inventoryButtonStyle == null) { + inventoryButtonStyle = skin.get("inventory", ButtonStyle.class); + } + + return inventoryButtonStyle; + } + public InventoryButton(Skin skin, World w, InventoryUI inv) { - super(skin, "inventory"); + super(getDefaultStyle(skin)); this.inventory = inv; this.world = w; diff --git a/blade-engine/src/com/bladecoder/engine/ui/InventoryUI.java b/blade-engine/src/com/bladecoder/engine/ui/InventoryUI.java index a65873331..a0aea8dea 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/InventoryUI.java +++ b/blade-engine/src/com/bladecoder/engine/ui/InventoryUI.java @@ -26,7 +26,7 @@ import com.badlogic.gdx.scenes.scene2d.InputListener; import com.badlogic.gdx.scenes.scene2d.actions.Actions; import com.badlogic.gdx.scenes.scene2d.ui.Button; -import com.badlogic.gdx.scenes.scene2d.ui.ImageButton.ImageButtonStyle; +import com.badlogic.gdx.scenes.scene2d.ui.Button.ButtonStyle; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.scenes.scene2d.utils.Drawable; import com.bladecoder.engine.model.ActorRenderer; @@ -368,7 +368,8 @@ static public class InventoryUIStyle { public Drawable background; /** Optional. */ public Drawable itemBackground; - public ImageButtonStyle menuButtonStyle; + public ButtonStyle menuButtonStyle; + public ButtonStyle inventoryButtonStyle; public InventoryUIStyle() { } @@ -377,6 +378,7 @@ public InventoryUIStyle(InventoryUIStyle style) { background = style.background; menuButtonStyle = style.menuButtonStyle; itemBackground = style.itemBackground; + inventoryButtonStyle = style.inventoryButtonStyle; } } } From f07854c3c96b6f9ce410eeb6806c4262af5942cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Garc=C3=ADa?= Date: Wed, 4 Dec 2019 17:25:27 +0100 Subject: [PATCH 065/147] Fix bug in IfAttrAction. --- .../engine/actions/IfAttrAction.java | 50 +++++++++++++++---- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/blade-engine/src/com/bladecoder/engine/actions/IfAttrAction.java b/blade-engine/src/com/bladecoder/engine/actions/IfAttrAction.java index c4855efb9..7fa160ddd 100644 --- a/blade-engine/src/com/bladecoder/engine/actions/IfAttrAction.java +++ b/blade-engine/src/com/bladecoder/engine/actions/IfAttrAction.java @@ -59,24 +59,36 @@ public boolean run(VerbRunner cb) { Scene s = actor.getScene(w); final String actorId = actor.getActorId(); - BaseActor a = s.getActor(actorId, true); - if (a == null) { - EngineLogger.error(getClass() + "- No not found: " + actorId); - return false; - } + if (attr.equals(ActorAttribute.STATE)) { + BaseActor a = s.getActor(actorId, true); + if (a == null || !(a instanceof InteractiveActor)) { + EngineLogger.error(getClass() + "- No 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() + "- No 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() + "- No not found: " + actorId); + return false; + } + boolean val = Boolean.parseBoolean(value); if (a instanceof InteractiveActor) { @@ -105,8 +117,7 @@ public boolean run(VerbRunner cb) { SpriteActor item = null; - if (a != null) - item = inventory.get(a.getId()); + item = inventory.get(actorId); if ((val && item == null) || (!val && item != null)) { gotoElse(cb); @@ -130,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) { @@ -152,6 +176,12 @@ public boolean run(VerbRunner 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) { From 63c90771e84e4aaf329117c0258d35690f89f614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Garc=C3=ADa?= Date: Thu, 5 Dec 2019 17:20:20 +0100 Subject: [PATCH 066/147] Better code to define Ink external functions. --- .../engine/ink/ExternalFunctions.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/blade-engine/src/com/bladecoder/engine/ink/ExternalFunctions.java b/blade-engine/src/com/bladecoder/engine/ink/ExternalFunctions.java index a278fcef0..69086b987 100644 --- a/blade-engine/src/com/bladecoder/engine/ink/ExternalFunctions.java +++ b/blade-engine/src/com/bladecoder/engine/ink/ExternalFunctions.java @@ -5,7 +5,7 @@ import com.bladecoder.engine.model.Scene; import com.bladecoder.engine.model.World; import com.bladecoder.engine.util.EngineLogger; -import com.bladecoder.ink.runtime.Story.ExternalFunction; +import com.bladecoder.ink.runtime.Story.ExternalFunction1; public class ExternalFunctions { @@ -18,12 +18,10 @@ public void bindExternalFunctions(final World w, InkManager ink) throws Exceptio this.inkManager = ink; - inkManager.getStory().bindExternalFunction("inInventory", new ExternalFunction() { + inkManager.getStory().bindExternalFunction("inInventory", new ExternalFunction1() { @Override - public Object call(Object[] args) throws Exception { - String actor = args[0].toString(); - + public Boolean call(String actor) throws Exception { if (actor.charAt(0) == '>') actor = actor.substring(1); @@ -31,11 +29,11 @@ public Object call(Object[] args) throws Exception { } }); - inkManager.getStory().bindExternalFunction("getActorState", new ExternalFunction() { + inkManager.getStory().bindExternalFunction("getActorState", new ExternalFunction1() { @Override - public Object call(Object[] args) throws Exception { - SceneActorRef actor = new SceneActorRef(args[0].toString()); + public String call(String act) throws Exception { + SceneActorRef actor = new SceneActorRef(act); final Scene s = actor.getScene(w); String actorId = actor.getActorId(); @@ -51,11 +49,10 @@ public Object call(Object[] args) throws Exception { } }); - inkManager.getStory().bindExternalFunction("getSceneState", new ExternalFunction() { + inkManager.getStory().bindExternalFunction("getSceneState", new ExternalFunction1() { @Override - public Object call(Object[] args) throws Exception { - String scene = args[0].toString(); + public String call(String scene) throws Exception { final Scene s = w.getScene(scene); if (s == null) { From f0f6ba3cd4579916bc330802565da44a34df1bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Garc=C3=ADa?= Date: Tue, 10 Dec 2019 18:18:06 +0100 Subject: [PATCH 067/147] Prepare for v3.2.2 --- CHANGELOG.md | 5 +++++ .../src/main/resources/projectTmpl/android/build.gradle | 8 +++++++- adventure-editor/src/main/resources/versions.properties | 2 +- gradle.properties | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fab236955..e9dc5f3d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [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. diff --git a/adventure-editor/src/main/resources/projectTmpl/android/build.gradle b/adventure-editor/src/main/resources/projectTmpl/android/build.gradle index 8a0397ce1..b82aa97b2 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 16 targetSdkVersion '%API_LEVEL%' versionName version @@ -130,6 +130,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") diff --git a/adventure-editor/src/main/resources/versions.properties b/adventure-editor/src/main/resources/versions.properties index 85bfd59bc..722821eb8 100644 --- a/adventure-editor/src/main/resources/versions.properties +++ b/adventure-editor/src/main/resources/versions.properties @@ -5,4 +5,4 @@ buildToolsVersion=28.0.3 libgdxVersion=1.9.10 roboVMGradlePluginVersion=2.3.7 roboVMVersion=2.3.7 -version=3.2.2-SNAPSHOT +version=3.2.2 diff --git a/gradle.properties b/gradle.properties index 963c43dbd..726873d03 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=3.2.2-SNAPSHOT +version=3.2.2 libgdxVersion=1.9.10 roboVMVersion=2.3.7 roboVMGradlePluginVersion=2.3.7 From 1d86cd51760a4f032214495bc27f500a64bd98eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Garc=C3=ADa?= Date: Wed, 18 Dec 2019 19:31:49 +0100 Subject: [PATCH 068/147] Save callbacks which aren't int he current scene. --- .../engine/spine/SpineRenderer.java | 7 +++-- .../engine/actions/BaseCallbackAction.java | 14 +++++---- .../com/bladecoder/engine/anim/Timers.java | 26 +++++++++------- .../src/com/bladecoder/engine/anim/Tween.java | 30 +++++++++++-------- .../com/bladecoder/engine/anim/WalkTween.java | 16 ++++++---- .../com/bladecoder/engine/ink/InkManager.java | 6 ++-- .../com/bladecoder/engine/model/Scene.java | 7 +++++ .../engine/model/Sprite3DRenderer.java | 5 ++-- .../src/com/bladecoder/engine/model/Text.java | 11 +++++-- .../bladecoder/engine/model/Transition.java | 15 ++++++---- .../src/com/bladecoder/engine/model/Verb.java | 17 +++++++---- .../ActionCallbackSerializer.java | 8 ++--- .../engine/serialization/BladeJson.java | 12 ++++++++ 13 files changed, 114 insertions(+), 60 deletions(-) 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 f44f0e5f1..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 @@ -704,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); @@ -733,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/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/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..f0d53fb5d 100644 --- a/blade-engine/src/com/bladecoder/engine/anim/WalkTween.java +++ b/blade-engine/src/com/bladecoder/engine/anim/WalkTween.java @@ -24,6 +24,8 @@ import com.bladecoder.engine.actions.ActionCallback; import com.bladecoder.engine.assets.EngineAssetManager; 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.InterpolationMode; @@ -64,7 +66,7 @@ private void walkToNextStep(CharacterActor target) { float s0 = 1.0f; float sf = 1.0f; - + if (target.getFakeDepth()) { s0 = target.getScene().getFakeDepthScale(p0.y); sf = target.getScene().getFakeDepthScale(pf.y); @@ -132,8 +134,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 +150,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/ink/InkManager.java b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java index ba6f377b7..83b926be6 100644 --- a/blade-engine/src/com/bladecoder/engine/ink/InkManager.java +++ b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java @@ -239,7 +239,7 @@ 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; @@ -505,10 +505,10 @@ 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"); diff --git a/blade-engine/src/com/bladecoder/engine/model/Scene.java b/blade-engine/src/com/bladecoder/engine/model/Scene.java index 47910d19f..c404eb711 100644 --- a/blade-engine/src/com/bladecoder/engine/model/Scene.java +++ b/blade-engine/src/com/bladecoder/engine/model/Scene.java @@ -696,6 +696,8 @@ 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); @@ -753,12 +755,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); @@ -868,5 +873,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/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/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/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/Verb.java b/blade-engine/src/com/bladecoder/engine/model/Verb.java index 9bdde2918..76458301b 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; @@ -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/serialization/ActionCallbackSerializer.java b/blade-engine/src/com/bladecoder/engine/serialization/ActionCallbackSerializer.java index 5fd925a2e..c4fb2fea3 100644 --- a/blade-engine/src/com/bladecoder/engine/serialization/ActionCallbackSerializer.java +++ b/blade-engine/src/com/bladecoder/engine/serialization/ActionCallbackSerializer.java @@ -184,7 +184,7 @@ private static String find(ActionCallback cb, Inventory inv) { * @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) @@ -209,8 +209,6 @@ public static String find(World w, ActionCallback cb) { return id; // search in scene verbs - Scene s = w.getCurrentScene(); - id = find(cb, s); if (id != null) @@ -250,13 +248,11 @@ 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)) { diff --git a/blade-engine/src/com/bladecoder/engine/serialization/BladeJson.java b/blade-engine/src/com/bladecoder/engine/serialization/BladeJson.java index 5c22c917f..9fd280883 100644 --- a/blade-engine/src/com/bladecoder/engine/serialization/BladeJson.java +++ b/blade-engine/src/com/bladecoder/engine/serialization/BladeJson.java @@ -13,6 +13,7 @@ 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; @@ -34,6 +35,9 @@ 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(); @@ -86,6 +90,14 @@ 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); } From 867638c59a43d5bb34924549ce39959f2500a0cf Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Thu, 19 Dec 2019 01:02:56 +0100 Subject: [PATCH 069/147] Update version. --- adventure-editor/src/main/resources/versions.properties | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/adventure-editor/src/main/resources/versions.properties b/adventure-editor/src/main/resources/versions.properties index 722821eb8..41edc6038 100644 --- a/adventure-editor/src/main/resources/versions.properties +++ b/adventure-editor/src/main/resources/versions.properties @@ -5,4 +5,4 @@ buildToolsVersion=28.0.3 libgdxVersion=1.9.10 roboVMGradlePluginVersion=2.3.7 roboVMVersion=2.3.7 -version=3.2.2 +version=3.2.3-SNAPSHOT diff --git a/gradle.properties b/gradle.properties index 726873d03..2c6f5d382 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=3.2.2 +version=3.2.3-SNAPSHOT libgdxVersion=1.9.10 roboVMVersion=2.3.7 roboVMGradlePluginVersion=2.3.7 From 670e527aedfaf891cbacd8a75d3132b93359af7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Garc=C3=ADa?= Date: Thu, 19 Dec 2019 19:35:41 +0100 Subject: [PATCH 070/147] Add init verb param to the Leave action. --- .../engine/actions/LeaveAction.java | 12 +++-- .../com/bladecoder/engine/ink/InkManager.java | 15 +++--- .../com/bladecoder/engine/model/Scene.java | 4 -- .../com/bladecoder/engine/model/World.java | 24 +++++++-- .../serialization/WorldSerialization.java | 4 +- .../com/bladecoder/engine/ui/DebugScreen.java | 50 +++++++++++-------- 6 files changed, 66 insertions(+), 43 deletions(-) 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/ink/InkManager.java b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java index 83b926be6..c09e810f1 100644 --- a/blade-engine/src/com/bladecoder/engine/ink/InkManager.java +++ b/blade-engine/src/com/bladecoder/engine/ink/InkManager.java @@ -290,21 +290,18 @@ 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) { // Try to create action by default diff --git a/blade-engine/src/com/bladecoder/engine/model/Scene.java b/blade-engine/src/com/bladecoder/engine/model/Scene.java index c404eb711..d2707bd0b 100644 --- a/blade-engine/src/com/bladecoder/engine/model/Scene.java +++ b/blade-engine/src/com/bladecoder/engine/model/Scene.java @@ -188,10 +188,6 @@ public void init() { timers.clear(); textManager.reset(); - - // Run INIT action - if (getVerb("init") != null) - runVerb("init"); } public VerbManager getVerbManager() { diff --git a/blade-engine/src/com/bladecoder/engine/model/World.java b/blade-engine/src/com/bladecoder/engine/model/World.java index 3d0a345ee..3fe38bd34 100644 --- a/blade-engine/src/com/bladecoder/engine/model/World.java +++ b/blade-engine/src/com/bladecoder/engine/model/World.java @@ -122,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() { @@ -268,6 +272,8 @@ 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) { @@ -275,9 +281,18 @@ else if (customProperties.get(WorldProperties.SAVED_GAME_VERSION.toString()) != 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) @@ -374,7 +389,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(); @@ -412,6 +427,7 @@ public void setCurrentScene(Scene scene, boolean init) { } currentScene = scene; + this.initVerb = initVerb; musicManager.leaveScene(currentScene.getMusicDesc()); } @@ -451,14 +467,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/serialization/WorldSerialization.java b/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java index 4ef0f213f..4850b7e88 100644 --- a/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java +++ b/blade-engine/src/com/bladecoder/engine/serialization/WorldSerialization.java @@ -155,9 +155,9 @@ 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); w.getI18N().loadChapter(EngineAssetManager.MODEL_DIR + chapterName); diff --git a/blade-engine/src/com/bladecoder/engine/ui/DebugScreen.java b/blade-engine/src/com/bladecoder/engine/ui/DebugScreen.java index 5ff40a6f4..b016a9d7a 100644 --- a/blade-engine/src/com/bladecoder/engine/ui/DebugScreen.java +++ b/blade-engine/src/com/bladecoder/engine/ui/DebugScreen.java @@ -116,6 +116,7 @@ public boolean keyUp(InputEvent event, int keycode) { 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 +126,7 @@ public void clicked(InputEvent event, float x, float y) { Table header = new Table(); header.padBottom(margin); - Container