diff --git a/.classpath b/.classpath
index 520551c..e6df400 100644
--- a/.classpath
+++ b/.classpath
@@ -4,6 +4,7 @@
+
@@ -28,4 +29,4 @@
-
+
\ No newline at end of file
diff --git a/ATCS_JAR.jardesc b/ATCS_JAR.jardesc
index c177a5a..0624630 100644
--- a/ATCS_JAR.jardesc
+++ b/ATCS_JAR.jardesc
@@ -1,7 +1,7 @@
-
+
-
-
+
+
@@ -11,9 +11,9 @@
-
-
-
-
+
+
+
+
diff --git a/folderIconBase.xcf b/folderIconBase.xcf
index 6ee67cf..1a0a6c2 100644
Binary files a/folderIconBase.xcf and b/folderIconBase.xcf differ
diff --git a/hacked-libtiled/tiled/core/TileSet.java b/hacked-libtiled/tiled/core/TileSet.java
index afd99af..fcacaf7 100644
--- a/hacked-libtiled/tiled/core/TileSet.java
+++ b/hacked-libtiled/tiled/core/TileSet.java
@@ -98,27 +98,42 @@ public class TileSet implements Iterable
File f = new File(imgFilename);
- Image image = ImageIO.read(f.getCanonicalFile());
+ BufferedImage image = ImageIO.read(f.getCanonicalFile());
if (image == null) {
- throw new IOException("Failed to load " + tilebmpFile);
+ throw new IOException("Failed to load " + imgFilename);
}
- Toolkit tk = Toolkit.getDefaultToolkit();
+ tilebmpFile = f;
+ tileDimensions = new Rectangle(cutter.getTileDimensions());
+
+// Toolkit tk = Toolkit.getDefaultToolkit();
+//
+// if (transparentColor != null) {
+// int rgb = transparentColor.getRGB();
+// image = tk.createImage(
+// new FilteredImageSource(image.getSource(),
+// new TransparentImageFilter(rgb)));
+// }
+//
+// BufferedImage buffered = new BufferedImage(
+// image.getWidth(null),
+// image.getHeight(null),
+// BufferedImage.TYPE_INT_ARGB);
+// buffered.getGraphics().drawImage(image, 0, 0, null);
- if (transparentColor != null) {
- int rgb = transparentColor.getRGB();
- image = tk.createImage(
- new FilteredImageSource(image.getSource(),
- new TransparentImageFilter(rgb)));
- }
+ importTileBitmap(image, cutter);
+ }
+
+ public void weakImportTileBitmap(String imgFilename, TileCutter cutter)
+ throws IOException
+ {
+ setTilesetImageFilename(imgFilename);
+
+ File f = new File(imgFilename);
- BufferedImage buffered = new BufferedImage(
- image.getWidth(null),
- image.getHeight(null),
- BufferedImage.TYPE_INT_ARGB);
- buffered.getGraphics().drawImage(image, 0, 0, null);
+ tilebmpFile = f;
+ tileDimensions = new Rectangle(cutter.getTileDimensions());
- importTileBitmap(buffered, cutter);
}
public void loadFromProject(String name, TMXMap tmxMap, int tileWidth, int tileHeight) {
diff --git a/minify/com/whoischarles/util/json/Minify.java b/minify/com/whoischarles/util/json/Minify.java
new file mode 100644
index 0000000..bc7c688
--- /dev/null
+++ b/minify/com/whoischarles/util/json/Minify.java
@@ -0,0 +1,405 @@
+/**
+ * ----------------------
+ * Minify.java 2015-10-04
+ * ----------------------
+ *
+ * Copyright (c) 2015 Charles Bihis (www.whoischarles.com)
+ *
+ * This work is an adaptation of JSMin.java published by John Reilly which is a translation from C to Java of jsmin.c
+ * published by Douglas Crockford. Permission is hereby granted to use this Java version under the same conditions as
+ * the original jsmin.c on which all of these derivatives are based.
+ *
+ *
+ *
+ * ---------------------
+ * JSMin.java 2006-02-13
+ * ---------------------
+ *
+ * Copyright (c) 2006 John Reilly (www.inconspicuous.org)
+ *
+ * This work is a translation from C to Java of jsmin.c published by Douglas Crockford. Permission is hereby granted to
+ * use the Java version under the same conditions as the jsmin.c on which it is based.
+ *
+ *
+ *
+ * ------------------
+ * jsmin.c 2003-04-21
+ * ------------------
+ *
+ * Copyright (c) 2002 Douglas Crockford (www.crockford.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
+ * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * The Software shall be used for Good, not Evil.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.whoischarles.util.json;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PushbackInputStream;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Minify.java is written by Charles Bihis (www.whoischarles.com) and is adapted from JSMin.java written by John Reilly
+ * (www.inconspicuous.org) which is itself a translation of jsmin.c written by Douglas Crockford (www.crockford.com).
+ *
+ * @see http://www.unl.edu/ucomm/templatedependents/JSMin.java
+ * @see http://www.crockford.com/javascript/jsmin.c
+ */
+public class Minify {
+
+ private static final int EOF = -1;
+
+ private PushbackInputStream in;
+ private OutputStream out;
+ private int currChar;
+ private int nextChar;
+ private int line;
+ private int column;
+
+ public static enum Action {
+ OUTPUT_CURR, DELETE_CURR, DELETE_NEXT
+ }
+
+ public Minify() {
+ this.in = null;
+ this.out = null;
+ }
+
+ /**
+ * Minifies the input JSON string.
+ *
+ * Takes the input JSON string and deletes the characters which are insignificant to JavaScipt. Comments will be
+ * removed, tabs will be replaced with spaces, carriage returns will be replaced with line feeds, and most spaces
+ * and line feeds will be removed. The result will be returned.
+ *
+ * @param json The JSON string for which to minify
+ * @return A minified, yet functionally identical, version of the input JSON string
+ */
+ public String minify(String json) {
+ InputStream in = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8));
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ try {
+ minify(in, out);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+
+ return out.toString().trim();
+ }
+
+ /**
+ * Takes an input stream to a JSON string and outputs minified JSON to the output stream.
+ *
+ * Takes the input JSON via the input stream and deletes the characters which are insignificant to JavaScript.
+ * Comments will be removed, tabs will be replaced with spaaces, carriage returns will be replaced with line feeds,
+ * and most spaces and line feeds will be removed. The result is streamed to the output stream.
+ *
+ * @param in The InputStream from which to get the un-minified JSON
+ * @param out The OutputStream where the resulting minified JSON will be streamed to
+ * @throws IOException
+ * @throws UnterminatedRegExpLiteralException
+ * @throws UnterminatedCommentException
+ * @throws UnterminatedStringLiteralException
+ */
+ public void minify(InputStream in, OutputStream out) throws IOException, UnterminatedRegExpLiteralException,
+ UnterminatedCommentException,
+ UnterminatedStringLiteralException {
+
+ // Initialize
+ this.in = new PushbackInputStream(in);
+ this.out = out;
+ this.line = 0;
+ this.column = 0;
+// currChar = '\n';
+// action(Action.DELETE_NEXT);
+ currChar = get();
+ nextChar = peek();
+
+ // Process input
+ while (currChar != EOF) {
+ switch (currChar) {
+
+ case ' ':
+ if (isAlphanum(nextChar)) {
+ action(Action.OUTPUT_CURR);
+ } else {
+ action(Action.DELETE_CURR);
+ }
+ break;
+
+ case '\n':
+ switch (nextChar) {
+ case '{':
+ case '[':
+ case '(':
+ case '+':
+ case '-':
+ action(Action.OUTPUT_CURR);
+ break;
+ case ' ':
+ action(Action.DELETE_NEXT);
+ break;
+ default:
+ if (isAlphanum(nextChar)) {
+ action(Action.OUTPUT_CURR);
+ } else {
+ action(Action.DELETE_CURR);
+ }
+ }
+ break;
+
+ default:
+ switch (nextChar) {
+ case ' ':
+ if (isAlphanum(currChar)) {
+ action(Action.OUTPUT_CURR);
+ break;
+ }
+ action(Action.DELETE_NEXT);
+ break;
+ case '\n':
+ switch (currChar) {
+ case '}':
+ case ']':
+ case ')':
+ case '+':
+ case '-':
+ case '"':
+ case '\'':
+ action(Action.OUTPUT_CURR);
+ break;
+ default:
+ if (isAlphanum(currChar)) {
+ action(Action.OUTPUT_CURR);
+ } else {
+ action(Action.DELETE_NEXT);
+ }
+ }
+ break;
+ default:
+ action(Action.OUTPUT_CURR);
+ break;
+ }
+ }
+ }
+ out.flush();
+ }
+
+ /**
+ * Process the current character with an appropriate action.
+ *
+ * The action that occurs is determined by the current character. The options are:
+ *
+ * 1. Output currChar: output currChar, copy nextChar to currChar, get the next character and save it to nextChar
+ * 2. Delete currChar: copy nextChar to currChar, get the next character and save it to nextChar
+ * 3. Delete nextChar: get the next character and save it to nextChar
+ *
+ * This method essentially treats a string as a single character. Also recognizes regular expressions if they are
+ * preceded by '(', ',', or '='.
+ *
+ * @param action The action to perform
+ * @throws IOException
+ * @throws UnterminatedRegExpLiteralException
+ * @throws UnterminatedCommentException
+ * @throws UnterminatedStringLiteralException
+ */
+ private void action(Action action) throws IOException, UnterminatedRegExpLiteralException, UnterminatedCommentException,
+ UnterminatedStringLiteralException {
+
+ // Process action
+ switch (action) {
+
+ case OUTPUT_CURR:
+ out.write(currChar);
+
+ case DELETE_CURR:
+ currChar = nextChar;
+
+ if (currChar == '\'' || currChar == '"') {
+ for ( ; ; ) {
+ out.write(currChar);
+ currChar = get();
+ if (currChar == nextChar) {
+ break;
+ }
+ if (currChar <= '\n') {
+ throw new UnterminatedStringLiteralException(line,
+ column);
+ }
+ if (currChar == '\\') {
+ out.write(currChar);
+ currChar = get();
+ }
+ }
+ }
+
+ case DELETE_NEXT:
+ nextChar = next();
+ if (nextChar == '/'
+ && (currChar == '(' || currChar == ',' || currChar == '=' || currChar == ':')) {
+ out.write(currChar);
+ out.write(nextChar);
+ for ( ; ; ) {
+ currChar = get();
+ if (currChar == '/') {
+ break;
+ } else if (currChar == '\\') {
+ out.write(currChar);
+ currChar = get();
+ } else if (currChar <= '\n') {
+ throw new UnterminatedRegExpLiteralException(line,
+ column);
+ }
+ out.write(currChar);
+ }
+ nextChar = next();
+ }
+ }
+ }
+
+ /**
+ * Determines whether a given character is a letter, digit, underscore, dollar sign, or non-ASCII character.
+ *
+ * @param c The character to compare
+ * @return True if the character is a letter, digit, underscore, dollar sign, or non-ASCII character. False otherwise.
+ */
+ private boolean isAlphanum(int c) {
+ return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')
+ || c == '_' || c == '$' || c == '\\' || c > 126);
+ }
+
+ /**
+ * Returns the next character from the input stream.
+ *
+ * Will pop the next character from the input stack. If the character is a control character, translate it to a space
+ * or line feed.
+ *
+ * @return The next character from the input stream
+ * @throws IOException
+ */
+ private int get() throws IOException {
+ int c = in.read();
+
+ if (c == '\n') {
+ line++;
+ column = 0;
+ } else {
+ column++;
+ }
+
+ if (c >= ' ' || c == '\n' || c == EOF) {
+ return c;
+ }
+
+ if (c == '\r') {
+ column = 0;
+ return '\n';
+ }
+
+ return ' ';
+ }
+
+ /**
+ * Returns the next character from the input stream without popping it from the stack.
+ *
+ * @return The next character from the input stream
+ * @throws IOException
+ */
+ private int peek() throws IOException {
+ int lookaheadChar = in.read();
+ in.unread(lookaheadChar);
+ return lookaheadChar;
+ }
+
+ /**
+ * Get the next character from the input stream, excluding comments.
+ *
+ * Will read from the input stream via the get() method. Will exclude characters that are part of
+ * comments. peek() is used to se if a '/' is followed by a '/' or a '*' for the purpose of identifying
+ * comments.
+ *
+ * @return The next character from the input stream, excluding characters from comments
+ * @throws IOException
+ * @throws UnterminatedCommentException
+ */
+ private int next() throws IOException, UnterminatedCommentException {
+ int c = get();
+
+ if (c == '/') {
+ switch (peek()) {
+
+ case '/':
+ for ( ; ; ) {
+ c = get();
+ if (c <= '\n') {
+ return c;
+ }
+ }
+
+ case '*':
+ get();
+ for ( ; ; ) {
+ switch (get()) {
+ case '*':
+ if (peek() == '/') {
+ get();
+ return ' ';
+ }
+ break;
+ case EOF:
+ throw new UnterminatedCommentException(line, column);
+ }
+ }
+
+ default:
+ return c;
+ }
+
+ }
+ return c;
+ }
+
+ /**
+ * Exception to be thrown when an unterminated comment appears in the input.
+ */
+ public static class UnterminatedCommentException extends Exception {
+ public UnterminatedCommentException(int line, int column) {
+ super("Unterminated comment at line " + line + " and column " + column);
+ }
+ }
+
+ /**
+ * Exception to be thrown when an unterminated string literal appears in the input.
+ */
+ public static class UnterminatedStringLiteralException extends Exception {
+ public UnterminatedStringLiteralException(int line, int column) {
+ super("Unterminated string literal at line " + line + " and column " + column);
+ }
+ }
+
+ /**
+ * Exception to be thrown when an unterminated regular expression literal appears in the input.
+ */
+ public static class UnterminatedRegExpLiteralException extends Exception {
+ public UnterminatedRegExpLiteralException(int line, int column) {
+ super("Unterminated regular expression at line " + line + " and column " + column);
+ }
+ }
+}
\ No newline at end of file
diff --git a/packaging/ATCS_latest b/packaging/ATCS_latest
index 52c6119..19d7022 100644
--- a/packaging/ATCS_latest
+++ b/packaging/ATCS_latest
@@ -1 +1 @@
-v0.6.7
\ No newline at end of file
+v0.6.14
\ No newline at end of file
diff --git a/packaging/Windows/ATCS_Installer.nsi b/packaging/Windows/ATCS_Installer.nsi
index 581c9bc..3e98602 100644
--- a/packaging/Windows/ATCS_Installer.nsi
+++ b/packaging/Windows/ATCS_Installer.nsi
@@ -1,6 +1,6 @@
!include MUI2.nsh
-!define VERSION "0.6.8"
+!define VERSION "0.6.14"
!define TRAINER_VERSION "0.1.4"
!define JAVA_BIN "javaw"
diff --git a/res/LICENSE.minify b/res/LICENSE.minify
new file mode 100644
index 0000000..53fb404
--- /dev/null
+++ b/res/LICENSE.minify
@@ -0,0 +1,43 @@
+
+----------------------
+Minify.java 2015-10-04
+----------------------
+
+Copyright (c) 2015 Charles Bihis (www.whoischarles.com)
+
+This work is an adaptation of JSMin.java published by John Reilly which is a translation from C to Java of jsmin.c
+published by Douglas Crockford. Permission is hereby granted to use this Java version under the same conditions as
+the original jsmin.c on which all of these derivatives are based.
+
+
+---------------------
+JSMin.java 2006-02-13
+---------------------
+
+Copyright (c) 2006 John Reilly (www.inconspicuous.org)
+
+This work is a translation from C to Java of jsmin.c published by Douglas Crockford. Permission is hereby granted to
+use the Java version under the same conditions as the jsmin.c on which it is based.
+
+
+
+------------------
+jsmin.c 2003-04-21
+------------------
+
+Copyright (c) 2002 Douglas Crockford (www.crockford.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
+Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/res/spritesheets.properties b/res/spritesheets.properties
index 771c6ad..78000a8 100644
--- a/res/spritesheets.properties
+++ b/res/spritesheets.properties
@@ -84,4 +84,5 @@ atcs.spritesheet.effect_bluetentacle.animate=true
atcs.spritesheet.effect_heal2.animate=true
atcs.spritesheet.effect_poison1.animate=true
atcs.spritesheet.effect_tometik1.animate=true
-atcs.spritesheet.effect_tometik2.animate=true
\ No newline at end of file
+atcs.spritesheet.effect_tometik2.animate=true
+atcs.spritesheet.monsters_guynmart.category=monster
diff --git a/siphash-zackehh/src/main/java/com/zackehh/siphash/SipHash.java b/siphash-zackehh/src/main/java/com/zackehh/siphash/SipHash.java
index d6d9da3..c82cb1f 100644
--- a/siphash-zackehh/src/main/java/com/zackehh/siphash/SipHash.java
+++ b/siphash-zackehh/src/main/java/com/zackehh/siphash/SipHash.java
@@ -1,6 +1,11 @@
package com.zackehh.siphash;
-import static com.zackehh.siphash.SipHashConstants.*;
+import static com.zackehh.siphash.SipHashConstants.DEFAULT_C;
+import static com.zackehh.siphash.SipHashConstants.DEFAULT_D;
+import static com.zackehh.siphash.SipHashConstants.INITIAL_V0;
+import static com.zackehh.siphash.SipHashConstants.INITIAL_V1;
+import static com.zackehh.siphash.SipHashConstants.INITIAL_V2;
+import static com.zackehh.siphash.SipHashConstants.INITIAL_V3;
/**
* Main entry point for SipHash, providing a basic hash
diff --git a/src/com/gpl/rpg/atcontentstudio/ATContentStudio.java b/src/com/gpl/rpg/atcontentstudio/ATContentStudio.java
index 3965439..7ece794 100644
--- a/src/com/gpl/rpg/atcontentstudio/ATContentStudio.java
+++ b/src/com/gpl/rpg/atcontentstudio/ATContentStudio.java
@@ -43,18 +43,18 @@ import org.eclipse.jgit.lib.ReflogReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
+import prefuse.data.expression.parser.ExpressionParser;
+
import com.gpl.rpg.atcontentstudio.model.Workspace;
import com.gpl.rpg.atcontentstudio.ui.StudioFrame;
import com.gpl.rpg.atcontentstudio.ui.WorkerDialog;
import com.gpl.rpg.atcontentstudio.ui.WorkspaceSelector;
-import prefuse.data.expression.parser.ExpressionParser;
-
public class ATContentStudio {
public static final String APP_NAME = "Andor's Trail Content Studio";
- public static final String APP_VERSION = "v0.6.8";
+ public static final String APP_VERSION = "v0.6.14";
public static final String CHECK_UPDATE_URL = "https://andorstrail.com/static/ATCS_latest";
public static final String DOWNLOAD_URL = "https://andorstrail.com/viewtopic.php?f=6&t=4806";
diff --git a/src/com/gpl/rpg/atcontentstudio/img/bookmark_active.png b/src/com/gpl/rpg/atcontentstudio/img/bookmark_active.png
new file mode 100644
index 0000000..4ad606c
Binary files /dev/null and b/src/com/gpl/rpg/atcontentstudio/img/bookmark_active.png differ
diff --git a/src/com/gpl/rpg/atcontentstudio/img/bookmark_inactive.png b/src/com/gpl/rpg/atcontentstudio/img/bookmark_inactive.png
new file mode 100644
index 0000000..28bf189
Binary files /dev/null and b/src/com/gpl/rpg/atcontentstudio/img/bookmark_inactive.png differ
diff --git a/src/com/gpl/rpg/atcontentstudio/img/folder_bookmark_closed.png b/src/com/gpl/rpg/atcontentstudio/img/folder_bookmark_closed.png
new file mode 100644
index 0000000..718313d
Binary files /dev/null and b/src/com/gpl/rpg/atcontentstudio/img/folder_bookmark_closed.png differ
diff --git a/src/com/gpl/rpg/atcontentstudio/img/folder_bookmark_open.png b/src/com/gpl/rpg/atcontentstudio/img/folder_bookmark_open.png
new file mode 100644
index 0000000..a4c74b7
Binary files /dev/null and b/src/com/gpl/rpg/atcontentstudio/img/folder_bookmark_open.png differ
diff --git a/src/com/gpl/rpg/atcontentstudio/img/folder_map_closed.png b/src/com/gpl/rpg/atcontentstudio/img/folder_map_closed.png
index 91c6115..5a343ba 100644
Binary files a/src/com/gpl/rpg/atcontentstudio/img/folder_map_closed.png and b/src/com/gpl/rpg/atcontentstudio/img/folder_map_closed.png differ
diff --git a/src/com/gpl/rpg/atcontentstudio/img/folder_map_open.png b/src/com/gpl/rpg/atcontentstudio/img/folder_map_open.png
index df85204..9646243 100644
Binary files a/src/com/gpl/rpg/atcontentstudio/img/folder_map_open.png and b/src/com/gpl/rpg/atcontentstudio/img/folder_map_open.png differ
diff --git a/src/com/gpl/rpg/atcontentstudio/model/GameDataElement.java b/src/com/gpl/rpg/atcontentstudio/model/GameDataElement.java
index 534b715..5643f57 100644
--- a/src/com/gpl/rpg/atcontentstudio/model/GameDataElement.java
+++ b/src/com/gpl/rpg/atcontentstudio/model/GameDataElement.java
@@ -11,6 +11,8 @@ import java.util.concurrent.ConcurrentHashMap;
import javax.swing.tree.TreeNode;
+import com.gpl.rpg.atcontentstudio.model.bookmarks.BookmarkEntry;
+
public abstract class GameDataElement implements ProjectTreeNode, Serializable {
private static final long serialVersionUID = 2028934451226743389L;
@@ -31,6 +33,8 @@ public abstract class GameDataElement implements ProjectTreeNode, Serializable {
public boolean writable = false;
+ public BookmarkEntry bookmark = null;
+
//List of objects whose transition to "linked" state made them point to this instance.
private Map backlinks = new ConcurrentHashMap();
diff --git a/src/com/gpl/rpg/atcontentstudio/model/Project.java b/src/com/gpl/rpg/atcontentstudio/model/Project.java
index 69ea424..e8182ca 100644
--- a/src/com/gpl/rpg/atcontentstudio/model/Project.java
+++ b/src/com/gpl/rpg/atcontentstudio/model/Project.java
@@ -1,34 +1,54 @@
package com.gpl.rpg.atcontentstudio.model;
import java.awt.Image;
+import java.io.ByteArrayInputStream;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
+import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
-import java.util.Set;
import javax.swing.tree.TreeNode;
+import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
import org.json.simple.JSONArray;
+import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
import com.gpl.rpg.atcontentstudio.ATContentStudio;
import com.gpl.rpg.atcontentstudio.Notification;
import com.gpl.rpg.atcontentstudio.io.JsonPrettyWriter;
import com.gpl.rpg.atcontentstudio.io.SettingsSave;
import com.gpl.rpg.atcontentstudio.model.GameSource.Type;
+import com.gpl.rpg.atcontentstudio.model.bookmarks.BookmarksRoot;
import com.gpl.rpg.atcontentstudio.model.gamedata.ActorCondition;
import com.gpl.rpg.atcontentstudio.model.gamedata.Dialogue;
import com.gpl.rpg.atcontentstudio.model.gamedata.Droplist;
@@ -68,6 +88,8 @@ public class Project implements ProjectTreeNode, Serializable {
public GameSource referencedContent; //Pointers to base content
public transient GameSource alteredContent; //Copied from base content (does not overwrite yet)
public transient GameSource createdContent; //Stand-alone.
+ public transient BookmarksRoot bookmarks;
+
public SavedGamesSet saves; //For simulations.
@@ -116,6 +138,7 @@ public class Project implements ProjectTreeNode, Serializable {
alteredContent = new GameSource(this, GameSource.Type.altered);
createdContent = new GameSource(this, GameSource.Type.created);
+ bookmarks = new BookmarksRoot(this);
saves = new SavedGamesSet(this);
@@ -124,6 +147,7 @@ public class Project implements ProjectTreeNode, Serializable {
// v.add(referencedContent);
v.add(baseContent);
v.add(saves);
+ v.add(bookmarks);
linkAll();
@@ -243,6 +267,7 @@ public class Project implements ProjectTreeNode, Serializable {
// referencedContent.refreshTransients(this);
alteredContent = new GameSource(this, GameSource.Type.altered);
createdContent = new GameSource(this, GameSource.Type.created);
+ bookmarks = new BookmarksRoot(this);
saves.refreshTransients();
@@ -252,6 +277,7 @@ public class Project implements ProjectTreeNode, Serializable {
// v.add(referencedContent);
v.add(baseContent);
v.add(saves);
+ v.add(bookmarks);
linkAll();
@@ -951,6 +977,10 @@ public class Project implements ProjectTreeNode, Serializable {
fireElementAdded(node, getNodeIndex(node));
}
+ public void bookmark(GameDataElement gde) {
+ bookmarks.addBookmark(gde);
+ }
+
@Override
public GameDataSet getDataSet() {
@@ -1043,85 +1073,149 @@ public class Project implements ProjectTreeNode, Serializable {
}
}
- public void generateExportPackage(final File target) {
+ public void exportProjectAsZipPackage(final File target) {
WorkerDialog.showTaskMessage("Exporting project "+name+"...", ATContentStudio.frame, true, new Runnable() {
@Override
public void run() {
Notification.addInfo("Exporting project \""+name+"\" as "+target.getAbsolutePath());
- File tmpDir = new File(baseFolder, "tmp");
- FileUtils.deleteDir(tmpDir);
- tmpDir.mkdir();
- File tmpJsonDataDir = new File(tmpDir, GameDataSet.DEFAULT_REL_PATH_IN_SOURCE);
- tmpJsonDataDir.mkdirs();
-
- for (File createdJsonFile : createdContent.gameData.baseFolder.listFiles()) {
- FileUtils.copyFile(createdJsonFile, new File(tmpJsonDataDir, createdJsonFile.getName()));
- }
- writeAltered(alteredContent.gameData.actorConditions, baseContent.gameData.actorConditions, ActorCondition.class, tmpJsonDataDir);
- writeAltered(alteredContent.gameData.dialogues, baseContent.gameData.dialogues, Dialogue.class, tmpJsonDataDir);
- writeAltered(alteredContent.gameData.droplists, baseContent.gameData.droplists, Droplist.class, tmpJsonDataDir);
- writeAltered(alteredContent.gameData.itemCategories, baseContent.gameData.itemCategories, ItemCategory.class, tmpJsonDataDir);
- writeAltered(alteredContent.gameData.items, baseContent.gameData.items, Item.class, tmpJsonDataDir);
- writeAltered(alteredContent.gameData.npcs, baseContent.gameData.npcs, NPC.class, tmpJsonDataDir);
- writeAltered(alteredContent.gameData.quests, baseContent.gameData.quests, Quest.class, tmpJsonDataDir);
-
- File tmpMapDir = new File(tmpDir, TMXMapSet.DEFAULT_REL_PATH_IN_SOURCE);
- tmpMapDir.mkdirs();
- for (File createdMapFile : createdContent.gameMaps.mapFolder.listFiles()) {
- FileUtils.copyFile(createdMapFile, new File(tmpMapDir, createdMapFile.getName()));
- }
- for (File alteredMapFile : alteredContent.gameMaps.mapFolder.listFiles()) {
- FileUtils.copyFile(alteredMapFile, new File(tmpMapDir, alteredMapFile.getName()));
- }
-
- if (!createdContent.worldmap.isEmpty() || !alteredContent.worldmap.isEmpty()) {
- try {
- Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
- doc.setXmlVersion("1.0");
- Element root = doc.createElement("worldmap");
- doc.appendChild(root);
-
- for (int i = 0; i < getWorldmapSegmentCount(); i++) {
- root.appendChild(getWorldmapSegment(i).toXmlElement(doc));
- }
-
- Worldmap.saveDocToFile(doc, new File(tmpMapDir, "worldmap.xml"));
- } catch (ParserConfigurationException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
+ File tmpDir = exportProjectToTmpDir();
FileUtils.writeToZip(tmpDir, target);
FileUtils.deleteDir(tmpDir);
Notification.addSuccess("Project \""+name+"\" exported as "+target.getAbsolutePath());
}
+
+
});
}
- @SuppressWarnings("rawtypes")
- public void writeAltered(GameDataCategory extends JSONElement> altered, GameDataCategory extends JSONElement> source, Class extends JSONElement> gdeClass, File targetFolder) {
- Set alteredFileNames = new LinkedHashSet();
- Map> toWrite = new LinkedHashMap>();
- for (JSONElement gde : altered) {
- alteredFileNames.add(gde.jsonFile.getName());
+ public void exportProjectOverGameSource(final File target) {
+ WorkerDialog.showTaskMessage("Exporting project "+name+"...", ATContentStudio.frame, true, new Runnable() {
+ @Override
+ public void run() {
+ Notification.addInfo("Exporting project \""+name+"\" into "+target.getAbsolutePath());
+
+ File tmpDir = exportProjectToTmpDir();
+
+ FileUtils.copyOver(tmpDir, target);
+ FileUtils.deleteDir(tmpDir);
+ Notification.addSuccess("Project \""+name+"\" exported into "+target.getAbsolutePath());
+ }
+
+
+ });
+ }
+
+ public File exportProjectToTmpDir() {
+ File tmpDir = new File(baseFolder, "tmp");
+ FileUtils.deleteDir(tmpDir);
+ tmpDir.mkdir();
+ File tmpJsonDataDir = new File(tmpDir, GameDataSet.DEFAULT_REL_PATH_IN_SOURCE);
+ tmpJsonDataDir.mkdirs();
+
+// for (File createdJsonFile : createdContent.gameData.baseFolder.listFiles()) {
+// FileUtils.copyFile(createdJsonFile, new File(tmpJsonDataDir, createdJsonFile.getName()));
+// }
+ Map, List> writtenFilesPerDataType = new LinkedHashMap, List>();
+ List writtenFiles;
+ writtenFiles = writeDataDeltaForDataType(createdContent.gameData.actorConditions, alteredContent.gameData.actorConditions, baseContent.gameData.actorConditions, ActorCondition.class, tmpJsonDataDir);
+ writtenFilesPerDataType.put(ActorCondition.class, writtenFiles);
+ writtenFiles = writeDataDeltaForDataType(createdContent.gameData.dialogues, alteredContent.gameData.dialogues, baseContent.gameData.dialogues, Dialogue.class, tmpJsonDataDir);
+ writtenFilesPerDataType.put(Dialogue.class, writtenFiles);
+ writtenFiles = writeDataDeltaForDataType(createdContent.gameData.droplists, alteredContent.gameData.droplists, baseContent.gameData.droplists, Droplist.class, tmpJsonDataDir);
+ writtenFilesPerDataType.put(Droplist.class, writtenFiles);
+ writtenFiles = writeDataDeltaForDataType(createdContent.gameData.itemCategories, alteredContent.gameData.itemCategories, baseContent.gameData.itemCategories, ItemCategory.class, tmpJsonDataDir);
+ writtenFilesPerDataType.put(ItemCategory.class, writtenFiles);
+ writtenFiles = writeDataDeltaForDataType(createdContent.gameData.items, alteredContent.gameData.items, baseContent.gameData.items, Item.class, tmpJsonDataDir);
+ writtenFilesPerDataType.put(Item.class, writtenFiles);
+ writtenFiles = writeDataDeltaForDataType(createdContent.gameData.npcs, alteredContent.gameData.npcs, baseContent.gameData.npcs, NPC.class, tmpJsonDataDir);
+ writtenFilesPerDataType.put(NPC.class, writtenFiles);
+ writtenFiles = writeDataDeltaForDataType(createdContent.gameData.quests, alteredContent.gameData.quests, baseContent.gameData.quests, Quest.class, tmpJsonDataDir);
+ writtenFilesPerDataType.put(Quest.class, writtenFiles);
+
+ File tmpMapDir = new File(tmpDir, TMXMapSet.DEFAULT_REL_PATH_IN_SOURCE);
+ tmpMapDir.mkdirs();
+ writtenFiles = new LinkedList();
+ for (File createdMapFile : createdContent.gameMaps.mapFolder.listFiles()) {
+ if (createdMapFile.getName().equalsIgnoreCase("worldmap.xml")) continue;
+ FileUtils.copyFile(createdMapFile, new File(tmpMapDir, createdMapFile.getName()));
+ writtenFiles.add(createdMapFile.getName());
}
- for (String fName : alteredFileNames) {
+ for (File alteredMapFile : alteredContent.gameMaps.mapFolder.listFiles()) {
+ if (alteredMapFile.getName().equalsIgnoreCase("worldmap.xml")) continue;
+ FileUtils.copyFile(alteredMapFile, new File(tmpMapDir, alteredMapFile.getName()));
+ writtenFiles.add(alteredMapFile.getName());
+ }
+ writtenFilesPerDataType.put(TMXMap.class, writtenFiles);
+
+ if (sourceSetToUse == ResourceSet.gameData) {
+ writeResourceListXml(writtenFilesPerDataType, GameSource.DEFAULT_REL_PATH_FOR_GAME_RESOURCE, baseContent.baseFolder, tmpDir);
+ } else if (sourceSetToUse == ResourceSet.debugData) {
+ writeResourceListXml(writtenFilesPerDataType, GameSource.DEFAULT_REL_PATH_FOR_DEBUG_RESOURCE, baseContent.baseFolder, tmpDir);
+ }
+
+
+ if (!createdContent.worldmap.isEmpty() || !alteredContent.worldmap.isEmpty()) {
+ try {
+ Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
+ doc.setXmlVersion("1.0");
+ Element root = doc.createElement("worldmap");
+ doc.appendChild(root);
+
+ for (int i = 0; i < getWorldmapSegmentCount(); i++) {
+ root.appendChild(getWorldmapSegment(i).toXmlElement(doc));
+ }
+
+ Worldmap.saveDocToFile(doc, new File(tmpMapDir, "worldmap.xml"));
+ } catch (ParserConfigurationException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ return tmpDir;
+ }
+
+ @SuppressWarnings("rawtypes")
+ public List writeDataDeltaForDataType(GameDataCategory extends JSONElement> created, GameDataCategory extends JSONElement> altered, GameDataCategory extends JSONElement> source, Class extends JSONElement> gdeClass, File targetFolder) {
+ List filenamesToWrite = new LinkedList();
+ Map> dataToWritePerFilename = new LinkedHashMap>();
+ for (JSONElement gde : altered) {
+ if (!filenamesToWrite.contains(gde.jsonFile.getName())) {
+ filenamesToWrite.add(gde.jsonFile.getName());
+ }
+ }
+ for (JSONElement gde : created) {
+ if (!filenamesToWrite.contains(gde.jsonFile.getName())) {
+ filenamesToWrite.add(gde.jsonFile.getName());
+ }
+ }
+ for (String fName : filenamesToWrite) {
for (JSONElement gde : source) {
if (gde.jsonFile.getName().equals(fName)) {
- if (toWrite.get(fName) == null) {
- toWrite.put(fName, new ArrayList