Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb8dcb9fb4 | ||
|
|
5e73b59d06 | ||
|
|
bca28781bd | ||
|
|
899e94c5bc | ||
|
|
0a7cb40dbc | ||
|
|
bd8576df0c | ||
|
|
49f19abb91 | ||
|
|
300b7bbbdd | ||
|
|
bbee5bef25 | ||
|
|
1fc1cef233 |
34
.classpath
@@ -1,16 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="res"/>
|
||||
<classpathentry kind="src" path="hacked-libtiled"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="lib" path="lib/jide-oss.jar"/>
|
||||
<classpathentry kind="lib" path="lib/json_simple-1.1.jar"/>
|
||||
<classpathentry kind="lib" path="lib/junit-4.10.jar"/>
|
||||
<classpathentry kind="lib" path="lib/prefuse.jar"/>
|
||||
<classpathentry kind="lib" path="lib/rsyntaxtextarea.jar"/>
|
||||
<classpathentry kind="lib" path="lib/ui.jar"/>
|
||||
<classpathentry kind="lib" path="lib/bsh-2.0b4.jar"/>
|
||||
<classpathentry kind="lib" path="lib/AndorsTrainer_v0.1.3.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="res"/>
|
||||
<classpathentry kind="src" path="hacked-libtiled"/>
|
||||
<classpathentry kind="src" path="siphash-zackehh/src/main/java"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="lib" path="lib/jide-oss.jar"/>
|
||||
<classpathentry kind="lib" path="lib/json_simple-1.1.jar"/>
|
||||
<classpathentry kind="lib" path="lib/junit-4.10.jar"/>
|
||||
<classpathentry kind="lib" path="lib/prefuse.jar"/>
|
||||
<classpathentry kind="lib" path="lib/rsyntaxtextarea.jar"/>
|
||||
<classpathentry kind="lib" path="lib/ui.jar"/>
|
||||
<classpathentry kind="lib" path="lib/bsh-2.0b4.jar"/>
|
||||
<classpathentry kind="lib" path="lib/AndorsTrainer_v0.1.3.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jsoup-1.10.2.jar" sourcepath="lib/jsoup-1.10.2-sources.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<jardesc>
|
||||
<jar path="ATContentStudio/ATCS_v0.5.2.jar"/>
|
||||
<jar path="ATContentStudio/ATCS_v0.6.1.jar"/>
|
||||
<options buildIfNeeded="true" compress="true" descriptionLocation="/ATContentStudio/ATCS_JAR.jardesc" exportErrors="true" exportWarnings="true" includeDirectoryEntries="false" overwrite="false" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/>
|
||||
<storedRefactorings deprecationInfo="true" structuralOnly="false"/>
|
||||
<selectedProjects/>
|
||||
@@ -11,6 +11,7 @@
|
||||
</sealing>
|
||||
</manifest>
|
||||
<selectedElements exportClassFiles="true" exportJavaFiles="true" exportOutputFolder="false">
|
||||
<javaElement handleIdentifier="=ATContentStudio/siphash-zackehh\/src\/main\/java"/>
|
||||
<javaElement handleIdentifier="=ATContentStudio/res"/>
|
||||
<javaElement handleIdentifier="=ATContentStudio/src"/>
|
||||
<javaElement handleIdentifier="=ATContentStudio/hacked-libtiled"/>
|
||||
|
||||
BIN
lib/jsoup-1.10.2-sources.jar
Normal file
BIN
lib/jsoup-1.10.2.jar
Normal file
@@ -1,20 +1,20 @@
|
||||
@echo off
|
||||
|
||||
set ATCS_DIR=%~dp0
|
||||
set MAX_MEM=512M
|
||||
set CP=%ATCS_DIR%lib\*
|
||||
set JAVA=javaw.exe
|
||||
set JAVA_OPTS=
|
||||
set ENV_FILE=%ATCS_DIR%ATCS.env.bat
|
||||
set MAIN_CLASS=com.gpl.rpg.atcontentstudio.ATContentStudio
|
||||
set "ATCS_DIR=%~dp0"
|
||||
set "MAX_MEM=512M"
|
||||
set "CP=%ATCS_DIR%lib\*"
|
||||
set "JAVA=javaw.exe"
|
||||
set "JAVA_OPTS="
|
||||
set "ENV_FILE=%ATCS_DIR%ATCS.env.bat"
|
||||
set "MAIN_CLASS=com.gpl.rpg.atcontentstudio.ATContentStudio"
|
||||
|
||||
if exist "%ENV_FILE%" (
|
||||
call "%ENV_FILE%"
|
||||
) else (
|
||||
echo REM set MAX_MEM=%MAX_MEM% > "%ENV_FILE%"
|
||||
echo REM set JAVA=%JAVA% >> "%ENV_FILE%"
|
||||
echo REM set JAVA_OPTS=%JAVA_OPTS% >> "%ENV_FILE%"
|
||||
echo. >> "%ENV_FILE%"
|
||||
echo REM set "MAX_MEM=%MAX_MEM%">"%ENV_FILE%"
|
||||
echo REM set "JAVA=%JAVA%">>"%ENV_FILE%"
|
||||
echo REM set "JAVA_OPTS=%JAVA_OPTS%">>"%ENV_FILE%"
|
||||
echo.>>"%ENV_FILE%"
|
||||
)
|
||||
|
||||
start "" "%JAVA%" %JAVA_OPTS% -Xmx%MAX_MEM% -cp "%CP%" %MAIN_CLASS%
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
@echo off
|
||||
|
||||
set ATCS_DIR=%~dp0
|
||||
set MAX_MEM=512M
|
||||
set CP=%ATCS_DIR%lib\*
|
||||
set JAVA=javaw.exe
|
||||
set JAVA_OPTS=
|
||||
set ENV_FILE=%ATCS_DIR%ATCS.env.bat
|
||||
set MAIN_CLASS=com.gpl.rpg.atcontentstudio.ATContentStudio
|
||||
set "ATCS_DIR=%~dp0"
|
||||
set "MAX_MEM=512M"
|
||||
set "CP=%ATCS_DIR%lib\*"
|
||||
set "JAVA=javaw.exe"
|
||||
set "JAVA_OPTS="
|
||||
set "ENV_FILE=%ATCS_DIR%ATCS.env.bat"
|
||||
set "MAIN_CLASS=com.gpl.rpg.atcontentstudio.ATContentStudio"
|
||||
|
||||
if exist "%ENV_FILE%" (
|
||||
call "%ENV_FILE%"
|
||||
) else (
|
||||
echo REM set MAX_MEM=%MAX_MEM% > "%ENV_FILE%"
|
||||
echo REM set JAVA=%JAVA% >> "%ENV_FILE%"
|
||||
echo REM set JAVA_OPTS=%JAVA_OPTS% >> "%ENV_FILE%"
|
||||
echo. >> "%ENV_FILE%"
|
||||
echo REM set "MAX_MEM=%MAX_MEM%">"%ENV_FILE%"
|
||||
echo REM set "JAVA=%JAVA%">>"%ENV_FILE%"
|
||||
echo REM set "JAVA_OPTS=%JAVA_OPTS%">>"%ENV_FILE%"
|
||||
echo.>>"%ENV_FILE%"
|
||||
)
|
||||
|
||||
start "" "%JAVA%" %JAVA_OPTS% -Xmx%MAX_MEM% -cp "%CP%" %MAIN_CLASS%
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
!include MUI2.nsh
|
||||
|
||||
!define VERSION "0.5.2"
|
||||
!define VERSION "0.6.1"
|
||||
!define TRAINER_VERSION "0.1.3"
|
||||
!define JAVA_BIN "javaw"
|
||||
|
||||
@@ -55,26 +55,28 @@ Section install
|
||||
SetOutPath $INSTDIR
|
||||
file "ATCS.ico"
|
||||
|
||||
Delete "$INSTDIR\lib\*"
|
||||
|
||||
Call GetJRE
|
||||
Pop $R0
|
||||
FileOpen $9 "ATCS.cmd" w
|
||||
FileWrite $9 '@echo off$\r$\n'
|
||||
FileWrite $9 '$\r$\n'
|
||||
FileWrite $9 'set ATCS_DIR=%~dp0$\r$\n'
|
||||
FileWrite $9 'set MAX_MEM=512M$\r$\n'
|
||||
FileWrite $9 'set CP=%ATCS_DIR%lib\*$\r$\n'
|
||||
FileWrite $9 'set JAVA=$R0$\r$\n'
|
||||
FileWrite $9 'set JAVA_OPTS=$\r$\n'
|
||||
FileWrite $9 'set ENV_FILE=%ATCS_DIR%ATCS.env.bat$\r$\n'
|
||||
FileWrite $9 'set MAIN_CLASS=com.gpl.rpg.atcontentstudio.ATContentStudio$\r$\n'
|
||||
FileWrite $9 'set "ATCS_DIR=%~dp0"$\r$\n'
|
||||
FileWrite $9 'set "MAX_MEM=512M"$\r$\n'
|
||||
FileWrite $9 'set "CP=%ATCS_DIR%lib\*"$\r$\n'
|
||||
FileWrite $9 'set "JAVA=$R0"$\r$\n'
|
||||
FileWrite $9 'set "JAVA_OPTS="$\r$\n'
|
||||
FileWrite $9 'set "ENV_FILE=%ATCS_DIR%ATCS.env.bat"$\r$\n'
|
||||
FileWrite $9 'set "MAIN_CLASS=com.gpl.rpg.atcontentstudio.ATContentStudio"$\r$\n'
|
||||
FileWrite $9 '$\r$\n'
|
||||
FileWrite $9 'if exist "%ENV_FILE%" ($\r$\n'
|
||||
FileWrite $9 ' call "%ENV_FILE%"$\r$\n'
|
||||
FileWrite $9 ') else ($\r$\n'
|
||||
FileWrite $9 ' echo REM set MAX_MEM=%MAX_MEM% > "%ENV_FILE%"$\r$\n'
|
||||
FileWrite $9 ' echo REM set JAVA=%JAVA% >> "%ENV_FILE%"$\r$\n'
|
||||
FileWrite $9 ' echo REM set JAVA_OPTS=%JAVA_OPTS% >> "%ENV_FILE%"$\r$\n'
|
||||
FileWrite $9 ' echo. >> "%ENV_FILE%"$\r$\n'
|
||||
FileWrite $9 ' echo REM set "MAX_MEM=%MAX_MEM%">"%ENV_FILE%"$\r$\n'
|
||||
FileWrite $9 ' echo REM set "JAVA=%JAVA%">>"%ENV_FILE%"$\r$\n'
|
||||
FileWrite $9 ' echo REM set "JAVA_OPTS=%JAVA_OPTS%">>"%ENV_FILE%"$\r$\n'
|
||||
FileWrite $9 ' echo.>>"%ENV_FILE%"$\r$\n'
|
||||
FileWrite $9 ')$\r$\n'
|
||||
FileWrite $9 '$\r$\n'
|
||||
FileWrite $9 'start "" "%JAVA%" %JAVA_OPTS% -Xmx%MAX_MEM% -cp "%CP%" %MAIN_CLASS%$\r$\n'
|
||||
@@ -90,6 +92,7 @@ Section install
|
||||
file "rsyntaxtextarea.jar"
|
||||
file "prefuse.jar"
|
||||
file "bsh-2.0b4.jar"
|
||||
file "jsoup-1.10.2.jar"
|
||||
|
||||
SetOutPath $INSTDIR
|
||||
|
||||
@@ -118,6 +121,7 @@ Section uninstall
|
||||
Delete "$INSTDIR\lib\rsyntaxtextarea.jar"
|
||||
Delete "$INSTDIR\lib\prefuse.jar"
|
||||
Delete "$INSTDIR\lib\bsh-2.0b4.jar"
|
||||
Delete "$INSTDIR\lib\jsoup-1.10.2.jar"
|
||||
RMDir "$INSTDIR\lib\"
|
||||
Delete "$INSTDIR\ATCS.ico"
|
||||
Delete "$INSTDIR\ATCS.cmd"
|
||||
|
||||
21
res/LICENSE.jsoup.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License
|
||||
|
||||
© 2009-2017, Jonathan Hedley <jonathan@hedley.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
21
res/LICENSE.siphash-zackehh.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Isaac Whitfield
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
19
siphash-zackehh/.gitignore
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
*.class
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
# IntelliJ
|
||||
.idea
|
||||
*.iml
|
||||
|
||||
# build
|
||||
target
|
||||
3
siphash-zackehh/.travis.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
language: java
|
||||
script:
|
||||
- mvn clean test jacoco:report coveralls:report
|
||||
21
siphash-zackehh/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Isaac Whitfield
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
77
siphash-zackehh/README.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# SipHash
|
||||
|
||||
[](https://travis-ci.org/zackehh/siphash-java) [](https://coveralls.io/github/zackehh/siphash-java?branch=master)
|
||||
|
||||
A Java implementation of the SipHash cryptographic hash family. Supports any variation, although defaults to the widely used SipHash-2-4. Can be used with either full input, or used as a streaming digest.
|
||||
|
||||
This library was heavily influenced by [veorq's C implementation](https://github.com/veorq/siphash) and [Forward C&C's reference implementation](http://www.forward.com.au/pfod/SipHashJavaLibrary/) - I just decided it was time a Java implementation of SipHash made it onto Maven :).
|
||||
|
||||
## Setup
|
||||
|
||||
`siphash` is available on Maven central, via Sonatype OSS:
|
||||
|
||||
```
|
||||
<dependency>
|
||||
<groupId>com.zackehh</groupId>
|
||||
<artifactId>siphash</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
There are two ways of using SipHash (see below). Both return a `SipHashResult` which can be used to retrieve the result in various forms. All constructors can take arguments to specify the compression rounds. For further usage, please visit the [documentation](http://www.javadoc.io/doc/com.zackehh/siphash).
|
||||
|
||||
#### Full Input Hash
|
||||
|
||||
The first is to simple create a `SipHash` instance and use it to repeatedly hash using the same key.
|
||||
|
||||
The internal state is immutable, so you can hash many inputs without having to recreate a new `SipHash` instance (unless you want a new key).
|
||||
|
||||
```java
|
||||
SipHash hasher = new SipHash("0123456789ABCDEF".getBytes());
|
||||
|
||||
SipHashResult result = hasher.hash("my-input".getBytes());
|
||||
|
||||
System.out.println(result.get()); // 182795880124085484 <-- this can overflow
|
||||
System.out.println(result.getHex()); // "2896be26d3374ec"
|
||||
System.out.println(result.getHex(true)); // "02896be26d3374ec"
|
||||
System.out.println(result.getHex(SipHashCase.UPPER)); // "2896BE26D3374EC"
|
||||
System.out.println(result.getHex(true, SipHashCase.UPPER)); // "02896BE26D3374EC"
|
||||
```
|
||||
|
||||
#### Streaming Hash
|
||||
|
||||
The second is to use the library as a streaming hash, meaning you can apply chunks of bytes to the hash as they become available.
|
||||
|
||||
Using this method you must create a new digest every time you want to hash a different input as the internal state is mutable.
|
||||
|
||||
```java
|
||||
SipHashDigest digest = new SipHashDigest("0123456789ABCDEF".getBytes());
|
||||
|
||||
digest.update("chu".getBytes());
|
||||
digest.update("nked".getBytes());
|
||||
digest.update(" string".getBytes());
|
||||
|
||||
SipHashResult result = digest.finish();
|
||||
|
||||
System.out.println(result.get()); // 3502906798476177428 <-- this can overflow
|
||||
System.out.println(result.getHex()); // "309cd32c8c793014"
|
||||
System.out.println(result.getHex(true)); // "309cd32c8c793014"
|
||||
System.out.println(result.getHex(SipHashCase.UPPER)); // "309CD32C8C793014"
|
||||
System.out.println(result.getHex(true, SipHashCase.UPPER)); // "309CD32C8C793014"
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
If you wish to contribute (awesome!), please file an issue first! All PRs should pass `mvn clean verify` and maintain 100% test coverage.
|
||||
|
||||
## Testing
|
||||
|
||||
Tests are run using `mvn`. I aim to maintain 100% coverage where possible (both line and branch).
|
||||
|
||||
Tests can be run as follows:
|
||||
|
||||
```bash
|
||||
$ mvn clean verify
|
||||
```
|
||||
171
siphash-zackehh/pom.xml
Normal file
@@ -0,0 +1,171 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.zackehh</groupId>
|
||||
<artifactId>siphash</artifactId>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>SipHash</name>
|
||||
<description>
|
||||
A SipHash implementation in Java.
|
||||
</description>
|
||||
<url>https://github.com/zackehh/siphash-java</url>
|
||||
|
||||
<properties>
|
||||
<jacoco.version>0.7.5.201505241946</jacoco.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<id>iwhitfield</id>
|
||||
<name>Isaac Whitfield</name>
|
||||
<email>iw@zackehh.com</email>
|
||||
<organization>Appcelerator, Inc.</organization>
|
||||
<organizationUrl>http://www.appcelerator.com</organizationUrl>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:git@github.com:zackehh/siphash-java.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:zackehh/siphash-java.git</developerConnection>
|
||||
<url>git@github.com:zackehh/siphash-java.git</url>
|
||||
</scm>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>MIT License</name>
|
||||
<url>http://www.opensource.org/licenses/mit-license.php</url>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>ossrh</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||
</snapshotRepository>
|
||||
<repository>
|
||||
<id>ossrh</id>
|
||||
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testng</groupId>
|
||||
<artifactId>testng</artifactId>
|
||||
<version>6.8.7</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.5</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.sonatype.plugins</groupId>
|
||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||
<version>1.6.3</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<serverId>ossrh</serverId>
|
||||
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
|
||||
<autoReleaseAfterClose>false</autoReleaseAfterClose>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.3</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>${jacoco.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>prepare-agent</id>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>report</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.eluder.coveralls</groupId>
|
||||
<artifactId>coveralls-maven-plugin</artifactId>
|
||||
<version>4.1.0</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
106
siphash-zackehh/src/main/java/com/zackehh/siphash/SipHash.java
Normal file
@@ -0,0 +1,106 @@
|
||||
package com.zackehh.siphash;
|
||||
|
||||
import static com.zackehh.siphash.SipHashConstants.*;
|
||||
|
||||
/**
|
||||
* Main entry point for SipHash, providing a basic hash
|
||||
* interface. Assuming you have your full String to hash,
|
||||
* you can simply provide it to ${@link SipHash#hash(byte[])}.
|
||||
*
|
||||
* This class can be initialized and stored in case the
|
||||
* developer wishes to use the same key over and over again.
|
||||
*
|
||||
* This avoids the overhead of having to create the initial
|
||||
* key over and over again.
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* List<String> inputs = Arrays.asList("input1", "input2", "input3");
|
||||
* SipHash hasher = new SipHash("this key is mine".getBytes());
|
||||
* for (int i = 0; i < inputs.size(); i++) {
|
||||
* hasher.hash(inputs.get(i));
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public class SipHash {
|
||||
|
||||
/**
|
||||
* The values of SipHash-c-d, to determine which of the SipHash
|
||||
* family we're using for this hash.
|
||||
*/
|
||||
private final int c, d;
|
||||
|
||||
/**
|
||||
* Initial seeded value of v0.
|
||||
*/
|
||||
private final long v0;
|
||||
|
||||
/**
|
||||
* Initial seeded value of v1.
|
||||
*/
|
||||
private final long v1;
|
||||
|
||||
/**
|
||||
* Initial seeded value of v2.
|
||||
*/
|
||||
private final long v2;
|
||||
|
||||
/**
|
||||
* Initial seeded value of v3.
|
||||
*/
|
||||
private final long v3;
|
||||
|
||||
/**
|
||||
* Accepts a 16 byte key input, and uses it to initialize
|
||||
* the state of the hash. This uses the default values of
|
||||
* c/d, meaning that we default to SipHash-2-4.
|
||||
*
|
||||
* @param key a 16 byte key input
|
||||
*/
|
||||
public SipHash(byte[] key){
|
||||
this(key, DEFAULT_C, DEFAULT_D);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts a 16 byte key input, and uses it to initialize
|
||||
* the state of the hash. This constructor allows for
|
||||
* providing the c/d values, allowing the developer to
|
||||
* select any of the SipHash family to use for hashing.
|
||||
*
|
||||
* @param key a 16 byte key input
|
||||
* @param c the number of compression rounds
|
||||
* @param d the number of finalization rounds
|
||||
*/
|
||||
public SipHash(byte[] key, int c, int d){
|
||||
this.c = c;
|
||||
this.d = d;
|
||||
|
||||
SipHashKey hashKey = new SipHashKey(key);
|
||||
|
||||
this.v0 = (INITIAL_V0 ^ hashKey.k0);
|
||||
this.v1 = (INITIAL_V1 ^ hashKey.k1);
|
||||
this.v2 = (INITIAL_V2 ^ hashKey.k0);
|
||||
this.v3 = (INITIAL_V3 ^ hashKey.k1);
|
||||
}
|
||||
|
||||
/**
|
||||
* The basic hash implementation provided in the library.
|
||||
* Assuming you have your full input, you can provide it and
|
||||
* it will be hashed based on the values which were provided
|
||||
* to the constructor of this class.
|
||||
*
|
||||
* @param data the bytes to hash
|
||||
* @return a ${@link SipHashResult} instance
|
||||
*/
|
||||
public SipHashResult hash(byte[] data) {
|
||||
SipHashDigest digest = new SipHashDigest(v0, v1, v2, v3, c, d);
|
||||
|
||||
for (byte aData : data) {
|
||||
digest.update(aData);
|
||||
}
|
||||
|
||||
return digest.finish();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.zackehh.siphash;
|
||||
|
||||
/**
|
||||
* A basic enum to determine the case of a String.
|
||||
*
|
||||
* A String can either be UPPER or LOWER case.
|
||||
*/
|
||||
public enum SipHashCase { UPPER, LOWER }
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.zackehh.siphash;
|
||||
|
||||
/**
|
||||
* Class containing several constants for use alongside
|
||||
* hashing. Fields such as initial states and defaults,
|
||||
* as they will not change throughout hashing.
|
||||
*/
|
||||
class SipHashConstants {
|
||||
|
||||
/**
|
||||
* This constructor is private, nobody should be
|
||||
* accessing it!
|
||||
*
|
||||
* @throws IllegalAccessException
|
||||
*/
|
||||
private SipHashConstants() throws IllegalAccessException {
|
||||
throw new IllegalAccessException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initial magic number for v0.
|
||||
*/
|
||||
static final long INITIAL_V0 = 0x736f6d6570736575L;
|
||||
|
||||
/**
|
||||
* Initial magic number for v1.
|
||||
*/
|
||||
static final long INITIAL_V1 = 0x646f72616e646f6dL;
|
||||
|
||||
/**
|
||||
* Initial magic number for v2.
|
||||
*/
|
||||
static final long INITIAL_V2 = 0x6c7967656e657261L;
|
||||
|
||||
/**
|
||||
* Initial magic number for v3.
|
||||
*/
|
||||
static final long INITIAL_V3 = 0x7465646279746573L;
|
||||
|
||||
/**
|
||||
* The default number of rounds of compression during per block.
|
||||
* This defaults to 2 as the default implementation is SipHash-2-4.
|
||||
*/
|
||||
static final int DEFAULT_C = 2;
|
||||
|
||||
/**
|
||||
* The default number of rounds of compression during finalization.
|
||||
* This defaults to 4 as the default implementation is SipHash-2-4.
|
||||
*/
|
||||
static final int DEFAULT_D = 4;
|
||||
|
||||
/**
|
||||
* Whether or not we should pad any hashes by default.
|
||||
*/
|
||||
static final boolean DEFAULT_PADDING = false;
|
||||
|
||||
/**
|
||||
* The default String casing for any output Hex Strings. We default
|
||||
* to lower case as it's the least expensive path.
|
||||
*/
|
||||
static final SipHashCase DEFAULT_CASE = SipHashCase.LOWER;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
package com.zackehh.siphash;
|
||||
|
||||
import static com.zackehh.siphash.SipHashConstants.DEFAULT_C;
|
||||
import static com.zackehh.siphash.SipHashConstants.DEFAULT_D;
|
||||
|
||||
/**
|
||||
* A streaming implementation of SipHash, to be used when
|
||||
* you don't have all input available at the same time. Chunks
|
||||
* of bytes can be applied as they're received, and will be hashed
|
||||
* accordingly.
|
||||
*
|
||||
* As with ${@link SipHash}, the compression and finalization rounds
|
||||
* can be customized.
|
||||
*/
|
||||
public class SipHashDigest {
|
||||
|
||||
/**
|
||||
* The values of SipHash-c-d, to determine which of the SipHash
|
||||
* family we're using for this hash.
|
||||
*/
|
||||
private final int c, d;
|
||||
|
||||
/**
|
||||
* Initial seeded value of v0.
|
||||
*/
|
||||
private long v0;
|
||||
|
||||
/**
|
||||
* Initial seeded value of v1.
|
||||
*/
|
||||
private long v1;
|
||||
|
||||
/**
|
||||
* Initial seeded value of v2.
|
||||
*/
|
||||
private long v2;
|
||||
|
||||
/**
|
||||
* Initial seeded value of v3.
|
||||
*/
|
||||
private long v3;
|
||||
|
||||
/**
|
||||
* A counter to keep track of the length of the input.
|
||||
*/
|
||||
private byte input_len = 0;
|
||||
|
||||
/**
|
||||
* A counter to keep track of the current position inside
|
||||
* of a chunk of bytes. Seeing as bytes are applied in chunks
|
||||
* of 8, this is necessary.
|
||||
*/
|
||||
private int m_idx = 0;
|
||||
|
||||
/**
|
||||
* The `m` value from the SipHash algorithm. Every 8 bytes, this
|
||||
* value will be applied to the current state of the hash.
|
||||
*/
|
||||
private long m;
|
||||
|
||||
/**
|
||||
* Accepts a 16 byte key input, and uses it to initialize
|
||||
* the state of the hash. This uses the default values of
|
||||
* c/d, meaning that we default to SipHash-2-4.
|
||||
*
|
||||
* @param key a 16 byte key input
|
||||
*/
|
||||
public SipHashDigest(byte[] key) {
|
||||
this(key, DEFAULT_C, DEFAULT_D);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts a 16 byte key input, and uses it to initialize
|
||||
* the state of the hash. This constructor allows for
|
||||
* providing the c/d values, allowing the developer to
|
||||
* select any of the SipHash family to use for hashing.
|
||||
*
|
||||
* @param key a 16 byte key input
|
||||
* @param c the number of compression rounds
|
||||
* @param d the number of finalization rounds
|
||||
*/
|
||||
public SipHashDigest(byte[] key, int c, int d) {
|
||||
this.c = c;
|
||||
this.d = d;
|
||||
|
||||
SipHashKey hashKey = new SipHashKey(key);
|
||||
|
||||
this.v0 = SipHashConstants.INITIAL_V0 ^ hashKey.k0;
|
||||
this.v1 = SipHashConstants.INITIAL_V1 ^ hashKey.k1;
|
||||
this.v2 = SipHashConstants.INITIAL_V2 ^ hashKey.k0;
|
||||
this.v3 = SipHashConstants.INITIAL_V3 ^ hashKey.k1;
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor is used by the ${@link SipHash} implementation,
|
||||
* and takes an initial (seeded) value of v0/v1/v2/v3. This is used
|
||||
* when the key has been pre-calculated. This constructor also
|
||||
* receives the values of `c` and `d` to use in this hash.
|
||||
*
|
||||
* @param v0 an initial seeded v0
|
||||
* @param v1 an initial seeded v1
|
||||
* @param v2 an initial seeded v2
|
||||
* @param v3 an initial seeded v3
|
||||
* @param c the number of compression rounds
|
||||
* @param d the number of finalization rounds
|
||||
*/
|
||||
SipHashDigest(long v0, long v1, long v2, long v3, int c, int d) {
|
||||
this.c = c;
|
||||
this.d = d;
|
||||
|
||||
this.v0 = v0;
|
||||
this.v1 = v1;
|
||||
this.v2 = v2;
|
||||
this.v3 = v3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current state of the hash with a single byte. This
|
||||
* is the streaming implementation which shifts as required to ensure
|
||||
* we can take an arbitrary number of bytes at any given time. We only
|
||||
* apply the block once the index (`m_idx`) has reached 8. The number
|
||||
* of compression rounds is determined by the `c` value passed in by
|
||||
* the developer.
|
||||
*
|
||||
* This method returns this instance, as a way of allowing the developer
|
||||
* to chain.
|
||||
*
|
||||
* @return a ${@link SipHashDigest} instance
|
||||
*/
|
||||
public SipHashDigest update(byte b) {
|
||||
input_len++;
|
||||
m |= (((long) b & 0xff) << (m_idx * 8));
|
||||
m_idx++;
|
||||
if (m_idx >= 8) {
|
||||
v3 ^= m;
|
||||
for (int i = 0; i < c; i++) {
|
||||
round();
|
||||
}
|
||||
v0 ^= m;
|
||||
m_idx = 0;
|
||||
m = 0;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method to allow passing a chunk of bytes at once, rather
|
||||
* than a byte at a time.
|
||||
*
|
||||
* @return a ${@link SipHashDigest} instance
|
||||
*/
|
||||
public SipHashDigest update(byte[] bytes) {
|
||||
for (byte b : bytes) {
|
||||
update(b);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalizes the hash by padding 0s until the next multiple of
|
||||
* 8 (as we operate in 8 byte chunks). The last byte added to
|
||||
* the hash is the length of the input, which we keep inside the
|
||||
* `input_len` counter. The number of rounds is based on the value
|
||||
* of `d` as specified by the developer.
|
||||
*
|
||||
* This method returns a ${@link SipHashResult}, as no further updates
|
||||
* should occur (i.e. the lack of chaining here shows we're done).
|
||||
*
|
||||
* @return a ${@link SipHashResult} instance
|
||||
*/
|
||||
public SipHashResult finish() {
|
||||
byte msgLenMod256 = input_len;
|
||||
|
||||
while (m_idx < 7) {
|
||||
update((byte) 0);
|
||||
}
|
||||
update(msgLenMod256);
|
||||
|
||||
v2 ^= 0xff;
|
||||
for (int i = 0; i < d; i++) {
|
||||
round();
|
||||
}
|
||||
|
||||
return new SipHashResult(v0 ^ v1 ^ v2 ^ v3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the equivalent of SipRound on the provided state.
|
||||
* This method affects the state of this digest, in that it
|
||||
* mutates the v states directly.
|
||||
*/
|
||||
private void round() {
|
||||
v0 += v1;
|
||||
v2 += v3;
|
||||
v1 = rotateLeft(v1, 13);
|
||||
v3 = rotateLeft(v3, 16);
|
||||
|
||||
v1 ^= v0;
|
||||
v3 ^= v2;
|
||||
v0 = rotateLeft(v0, 32);
|
||||
|
||||
v2 += v1;
|
||||
v0 += v3;
|
||||
v1 = rotateLeft(v1, 17);
|
||||
v3 = rotateLeft(v3, 21);
|
||||
|
||||
v1 ^= v2;
|
||||
v3 ^= v0;
|
||||
v2 = rotateLeft(v2, 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates an input number `val` left by `shift` number of bits. Bits which are
|
||||
* pushed off to the left are rotated back onto the right, making this a left
|
||||
* rotation (a circular shift).
|
||||
*
|
||||
* @param val the value to be shifted
|
||||
* @param shift how far left to shift
|
||||
* @return a long value once shifted
|
||||
*/
|
||||
private long rotateLeft(long val, int shift) {
|
||||
return (val << shift) | val >>> (64 - shift);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.zackehh.siphash;
|
||||
|
||||
/**
|
||||
* A container class to store both k0 and k1. These
|
||||
* values are created from a 16 byte key passed into
|
||||
* the constructor. This isn't ideal as it's another
|
||||
* alloc, but it'll do for now.
|
||||
*/
|
||||
class SipHashKey {
|
||||
|
||||
/**
|
||||
* The value of k0.
|
||||
*/
|
||||
final long k0;
|
||||
|
||||
/**
|
||||
* The value of k1.
|
||||
*/
|
||||
final long k1;
|
||||
|
||||
/**
|
||||
* Accepts a 16 byte input key and converts the
|
||||
* first and last 8 byte chunks to little-endian.
|
||||
* These values become k0 and k1.
|
||||
*
|
||||
* @param key the 16 byte key input
|
||||
*/
|
||||
public SipHashKey(byte[] key) {
|
||||
if (key.length != 16) {
|
||||
throw new IllegalArgumentException("Key must be exactly 16 bytes!");
|
||||
}
|
||||
this.k0 = bytesToLong(key, 0);
|
||||
this.k1 = bytesToLong(key, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a chunk of 8 bytes to a number in little-endian
|
||||
* format. Accepts an offset to determine where the chunk
|
||||
* begins in the byte array.
|
||||
*
|
||||
* @param b our byte array
|
||||
* @param offset the index to start at
|
||||
* @return a little-endian long representation
|
||||
*/
|
||||
private static long bytesToLong(byte[] b, int offset) {
|
||||
long m = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
m |= ((((long) b[i + offset]) & 0xff) << (8 * i));
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.zackehh.siphash;
|
||||
|
||||
import static com.zackehh.siphash.SipHashConstants.DEFAULT_CASE;
|
||||
import static com.zackehh.siphash.SipHashConstants.DEFAULT_PADDING;
|
||||
|
||||
/**
|
||||
* A container class of the result of a hash. This class exists
|
||||
* to allow the developer to retrieve the result in any format
|
||||
* they like. Currently available formats are `long` and ${@link java.lang.String}.
|
||||
* When retrieving as a String, the developer can specify the case
|
||||
* they want it in, and whether or not we should pad the left side
|
||||
* to 16 characters with 0s.
|
||||
*/
|
||||
public class SipHashResult {
|
||||
|
||||
/**
|
||||
* The internal hash result.
|
||||
*/
|
||||
private final long result;
|
||||
|
||||
/**
|
||||
* A package-private constructor, as only
|
||||
* SipHash should be creating results.
|
||||
*
|
||||
* @param result the result of a hash
|
||||
*/
|
||||
SipHashResult(long result){
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply returns the hash result as a long.
|
||||
*
|
||||
* @return the hash value as a long
|
||||
*/
|
||||
public long get(){
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result as a Hex String, using
|
||||
* the default padding and casing values.
|
||||
*
|
||||
* @return the hash value as a Hex String
|
||||
*/
|
||||
public String getHex(){
|
||||
return getHex(DEFAULT_PADDING, DEFAULT_CASE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result as a Hex String, using
|
||||
* a custom padding value and default casing value.
|
||||
*
|
||||
* @param padding whether or not to pad the string
|
||||
* @return the hash value as a Hex String
|
||||
*/
|
||||
public String getHex(boolean padding){
|
||||
return getHex(padding, DEFAULT_CASE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result as a Hex String, using
|
||||
* a default padding value and custom casing value.
|
||||
*
|
||||
* @param s_case the case to convert the output to
|
||||
* @return the hash value as a Hex String
|
||||
*/
|
||||
public String getHex(SipHashCase s_case){
|
||||
return getHex(DEFAULT_PADDING, s_case);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result as a Hex String, taking in
|
||||
* various arguments to customize the output further,
|
||||
* such as casing and padding.
|
||||
*
|
||||
* @param padding whether or not to pad the string
|
||||
* @param s_case the case to convert the output to
|
||||
* @return a Hex String in the custom format
|
||||
*/
|
||||
public String getHex(boolean padding, SipHashCase s_case){
|
||||
String str = Long.toHexString(get());
|
||||
if (padding) {
|
||||
str = leftPad(str, 16, "0");
|
||||
}
|
||||
if (s_case == SipHashCase.UPPER) {
|
||||
str = str.toUpperCase();
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modified for https://github.com/Zukero/ATCS
|
||||
* Replaces the StringUtils.leftPad from apache commons, to remove dependency.
|
||||
*
|
||||
* @param str the string to pad
|
||||
* @param len the total desired length
|
||||
* @param pad the padding string
|
||||
* @return str prefixed with enough repetitions of the pad to have a total length matching len
|
||||
*/
|
||||
public String leftPad(String str, int len, String pad) {
|
||||
StringBuilder sb = new StringBuilder(len);
|
||||
|
||||
int padlen = len - str.length();
|
||||
int partialPadLen = padlen % pad.length();
|
||||
int padCount = padlen / pad.length();
|
||||
|
||||
while (padCount >= 0) {
|
||||
sb.append(pad);
|
||||
padCount--;
|
||||
}
|
||||
|
||||
if (partialPadLen > 0) {
|
||||
sb.append(pad.substring(0, partialPadLen));
|
||||
}
|
||||
|
||||
sb.append(str);
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.zackehh.siphash;
|
||||
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class SipHashCaseTest {
|
||||
|
||||
@Test
|
||||
public void ensureAllValues() throws Exception {
|
||||
SipHashCase[] cases = SipHashCase.values();
|
||||
|
||||
Assert.assertEquals(cases[0], SipHashCase.UPPER);
|
||||
Assert.assertEquals(cases[1], SipHashCase.LOWER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureValueOf() throws Exception {
|
||||
Assert.assertEquals(SipHashCase.valueOf("UPPER"), SipHashCase.UPPER);
|
||||
Assert.assertEquals(SipHashCase.valueOf("LOWER"), SipHashCase.LOWER);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void invalidCaseValueOf() throws Exception {
|
||||
SipHashCase.valueOf("invalid");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.zackehh.siphash;
|
||||
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
public class SipHashConstantsTest {
|
||||
|
||||
@Test
|
||||
public void ensureAllConstants() throws Exception {
|
||||
Assert.assertEquals(SipHashConstants.INITIAL_V0, 0x736f6d6570736575L);
|
||||
Assert.assertEquals(SipHashConstants.INITIAL_V1, 0x646f72616e646f6dL);
|
||||
Assert.assertEquals(SipHashConstants.INITIAL_V2, 0x6c7967656e657261L);
|
||||
Assert.assertEquals(SipHashConstants.INITIAL_V3, 0x7465646279746573L);
|
||||
Assert.assertEquals(SipHashConstants.DEFAULT_C, 2);
|
||||
Assert.assertEquals(SipHashConstants.DEFAULT_D, 4);
|
||||
Assert.assertEquals(SipHashConstants.DEFAULT_CASE, SipHashCase.LOWER);
|
||||
Assert.assertEquals(SipHashConstants.DEFAULT_PADDING, false);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = InvocationTargetException.class)
|
||||
public void ensureCannotInstance() throws Exception {
|
||||
Constructor<SipHashConstants> ctor = SipHashConstants.class.getDeclaredConstructor();
|
||||
ctor.setAccessible(true);
|
||||
Assert.assertTrue(Modifier.isPrivate(ctor.getModifiers()));
|
||||
ctor.newInstance();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package com.zackehh.siphash;
|
||||
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class SipHashDigestTest {
|
||||
|
||||
@Test
|
||||
public void initializeDigestWithKey() throws Exception {
|
||||
SipHashDigest sipHash = new SipHashDigest("0123456789ABCDEF".getBytes());
|
||||
|
||||
long v0 = SipHashTestUtils.getPrivateField(sipHash, "v0", Long.class);
|
||||
long v1 = SipHashTestUtils.getPrivateField(sipHash, "v1", Long.class);
|
||||
long v2 = SipHashTestUtils.getPrivateField(sipHash, "v2", Long.class);
|
||||
long v3 = SipHashTestUtils.getPrivateField(sipHash, "v3", Long.class);
|
||||
|
||||
Assert.assertEquals(v0, 4925064773550298181L);
|
||||
Assert.assertEquals(v1, 2461839666708829781L);
|
||||
Assert.assertEquals(v2, 6579568090023412561L);
|
||||
Assert.assertEquals(v3, 3611922228250500171L);
|
||||
|
||||
int c = SipHashTestUtils.getPrivateField(sipHash, "c", Integer.class);
|
||||
int d = SipHashTestUtils.getPrivateField(sipHash, "d", Integer.class);
|
||||
|
||||
Assert.assertEquals(c, SipHashConstants.DEFAULT_C);
|
||||
Assert.assertEquals(d, SipHashConstants.DEFAULT_D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initializeDigestWithKeyAndCD() throws Exception {
|
||||
SipHashDigest sipHash = new SipHashDigest("0123456789ABCDEF".getBytes(), 4, 8);
|
||||
|
||||
long v0 = SipHashTestUtils.getPrivateField(sipHash, "v0", Long.class);
|
||||
long v1 = SipHashTestUtils.getPrivateField(sipHash, "v1", Long.class);
|
||||
long v2 = SipHashTestUtils.getPrivateField(sipHash, "v2", Long.class);
|
||||
long v3 = SipHashTestUtils.getPrivateField(sipHash, "v3", Long.class);
|
||||
|
||||
Assert.assertEquals(v0, 4925064773550298181L);
|
||||
Assert.assertEquals(v1, 2461839666708829781L);
|
||||
Assert.assertEquals(v2, 6579568090023412561L);
|
||||
Assert.assertEquals(v3, 3611922228250500171L);
|
||||
|
||||
int c = SipHashTestUtils.getPrivateField(sipHash, "c", Integer.class);
|
||||
int d = SipHashTestUtils.getPrivateField(sipHash, "d", Integer.class);
|
||||
|
||||
Assert.assertEquals(c, 4);
|
||||
Assert.assertEquals(d, 8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initializeDigestWithKeyThenHash() throws Exception {
|
||||
SipHashDigest sipHash = new SipHashDigest("0123456789ABCDEF".getBytes());
|
||||
|
||||
long v0 = SipHashTestUtils.getPrivateField(sipHash, "v0", Long.class);
|
||||
long v1 = SipHashTestUtils.getPrivateField(sipHash, "v1", Long.class);
|
||||
long v2 = SipHashTestUtils.getPrivateField(sipHash, "v2", Long.class);
|
||||
long v3 = SipHashTestUtils.getPrivateField(sipHash, "v3", Long.class);
|
||||
|
||||
Assert.assertEquals(v0, 4925064773550298181L);
|
||||
Assert.assertEquals(v1, 2461839666708829781L);
|
||||
Assert.assertEquals(v2, 6579568090023412561L);
|
||||
Assert.assertEquals(v3, 3611922228250500171L);
|
||||
|
||||
int c = SipHashTestUtils.getPrivateField(sipHash, "c", Integer.class);
|
||||
int d = SipHashTestUtils.getPrivateField(sipHash, "d", Integer.class);
|
||||
|
||||
Assert.assertEquals(c, SipHashConstants.DEFAULT_C);
|
||||
Assert.assertEquals(d, SipHashConstants.DEFAULT_D);
|
||||
|
||||
for(byte b : "zymotechnics".getBytes()){
|
||||
sipHash.update(b);
|
||||
}
|
||||
|
||||
SipHashResult hashResult = sipHash.finish();
|
||||
|
||||
Assert.assertEquals(hashResult.get(), 699588702094987020L);
|
||||
Assert.assertEquals(hashResult.getHex(), "9b57037cd3f8f0c");
|
||||
Assert.assertEquals(hashResult.getHex(true), "09b57037cd3f8f0c");
|
||||
Assert.assertEquals(hashResult.getHex(SipHashCase.UPPER), "9B57037CD3F8F0C");
|
||||
Assert.assertEquals(hashResult.getHex(true, SipHashCase.UPPER), "09B57037CD3F8F0C");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initializeStateWithKeyAndCDThenHash() throws Exception {
|
||||
SipHashDigest sipHash = new SipHashDigest("0123456789ABCDEF".getBytes(), 4, 8);
|
||||
|
||||
long v0 = SipHashTestUtils.getPrivateField(sipHash, "v0", Long.class);
|
||||
long v1 = SipHashTestUtils.getPrivateField(sipHash, "v1", Long.class);
|
||||
long v2 = SipHashTestUtils.getPrivateField(sipHash, "v2", Long.class);
|
||||
long v3 = SipHashTestUtils.getPrivateField(sipHash, "v3", Long.class);
|
||||
|
||||
Assert.assertEquals(v0, 4925064773550298181L);
|
||||
Assert.assertEquals(v1, 2461839666708829781L);
|
||||
Assert.assertEquals(v2, 6579568090023412561L);
|
||||
Assert.assertEquals(v3, 3611922228250500171L);
|
||||
|
||||
int c = SipHashTestUtils.getPrivateField(sipHash, "c", Integer.class);
|
||||
int d = SipHashTestUtils.getPrivateField(sipHash, "d", Integer.class);
|
||||
|
||||
Assert.assertEquals(c, 4);
|
||||
Assert.assertEquals(d, 8);
|
||||
|
||||
for(byte b : "zymotechnics".getBytes()){
|
||||
sipHash.update(b);
|
||||
}
|
||||
|
||||
SipHashResult hashResult = sipHash.finish();
|
||||
|
||||
Assert.assertEquals(hashResult.get(), -3891084581787974112L); // overflow
|
||||
Assert.assertEquals(hashResult.getHex(), "ca0017304f874620");
|
||||
Assert.assertEquals(hashResult.getHex(true), "ca0017304f874620");
|
||||
Assert.assertEquals(hashResult.getHex(SipHashCase.UPPER), "CA0017304F874620");
|
||||
Assert.assertEquals(hashResult.getHex(true, SipHashCase.UPPER), "CA0017304F874620");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateWithAByteArrayChunk() throws Exception {
|
||||
SipHashDigest sipHash = new SipHashDigest("0123456789ABCDEF".getBytes());
|
||||
|
||||
long v0 = SipHashTestUtils.getPrivateField(sipHash, "v0", Long.class);
|
||||
long v1 = SipHashTestUtils.getPrivateField(sipHash, "v1", Long.class);
|
||||
long v2 = SipHashTestUtils.getPrivateField(sipHash, "v2", Long.class);
|
||||
long v3 = SipHashTestUtils.getPrivateField(sipHash, "v3", Long.class);
|
||||
|
||||
Assert.assertEquals(v0, 4925064773550298181L);
|
||||
Assert.assertEquals(v1, 2461839666708829781L);
|
||||
Assert.assertEquals(v2, 6579568090023412561L);
|
||||
Assert.assertEquals(v3, 3611922228250500171L);
|
||||
|
||||
int c = SipHashTestUtils.getPrivateField(sipHash, "c", Integer.class);
|
||||
int d = SipHashTestUtils.getPrivateField(sipHash, "d", Integer.class);
|
||||
|
||||
Assert.assertEquals(c, SipHashConstants.DEFAULT_C);
|
||||
Assert.assertEquals(d, SipHashConstants.DEFAULT_D);
|
||||
|
||||
sipHash.update("zymo".getBytes());
|
||||
sipHash.update("techni".getBytes());
|
||||
sipHash.update("cs".getBytes());
|
||||
|
||||
SipHashResult hashResult = sipHash.finish();
|
||||
|
||||
Assert.assertEquals(hashResult.get(), 699588702094987020L);
|
||||
Assert.assertEquals(hashResult.getHex(), "9b57037cd3f8f0c");
|
||||
Assert.assertEquals(hashResult.getHex(true), "09b57037cd3f8f0c");
|
||||
Assert.assertEquals(hashResult.getHex(SipHashCase.UPPER), "9B57037CD3F8F0C");
|
||||
Assert.assertEquals(hashResult.getHex(true, SipHashCase.UPPER), "09B57037CD3F8F0C");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.zackehh.siphash;
|
||||
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class SipHashKeyTest {
|
||||
|
||||
@Test
|
||||
public void initializeWithKey() throws Exception {
|
||||
SipHashKey key = new SipHashKey("0123456789ABCDEF".getBytes());
|
||||
|
||||
long k0 = SipHashTestUtils.getPrivateField(key, "k0", Long.class);
|
||||
long k1 = SipHashTestUtils.getPrivateField(key, "k1", Long.class);
|
||||
|
||||
Assert.assertEquals(k0, 3978425819141910832L);
|
||||
Assert.assertEquals(k1, 5063528411713059128L);
|
||||
}
|
||||
|
||||
@Test(
|
||||
expectedExceptions = IllegalArgumentException.class,
|
||||
expectedExceptionsMessageRegExp = "Key must be exactly 16 bytes!"
|
||||
)
|
||||
public void initializeWithKeyTooLong() throws Exception {
|
||||
new SipHashKey("0123456789ABCDEFG".getBytes()); // 17 bytes
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.zackehh.siphash;
|
||||
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class SipHashTest {
|
||||
|
||||
@Test
|
||||
public void initializeStateWithKey() throws Exception {
|
||||
SipHash sipHash = new SipHash("0123456789ABCDEF".getBytes());
|
||||
|
||||
long v0 = SipHashTestUtils.getPrivateField(sipHash, "v0", Long.class);
|
||||
long v1 = SipHashTestUtils.getPrivateField(sipHash, "v1", Long.class);
|
||||
long v2 = SipHashTestUtils.getPrivateField(sipHash, "v2", Long.class);
|
||||
long v3 = SipHashTestUtils.getPrivateField(sipHash, "v3", Long.class);
|
||||
|
||||
Assert.assertEquals(v0, 4925064773550298181L);
|
||||
Assert.assertEquals(v1, 2461839666708829781L);
|
||||
Assert.assertEquals(v2, 6579568090023412561L);
|
||||
Assert.assertEquals(v3, 3611922228250500171L);
|
||||
|
||||
int c = SipHashTestUtils.getPrivateField(sipHash, "c", Integer.class);
|
||||
int d = SipHashTestUtils.getPrivateField(sipHash, "d", Integer.class);
|
||||
|
||||
Assert.assertEquals(c, SipHashConstants.DEFAULT_C);
|
||||
Assert.assertEquals(d, SipHashConstants.DEFAULT_D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initializeStateWithKeyAndCD() throws Exception {
|
||||
SipHash sipHash = new SipHash("0123456789ABCDEF".getBytes(), 4, 8);
|
||||
|
||||
long v0 = SipHashTestUtils.getPrivateField(sipHash, "v0", Long.class);
|
||||
long v1 = SipHashTestUtils.getPrivateField(sipHash, "v1", Long.class);
|
||||
long v2 = SipHashTestUtils.getPrivateField(sipHash, "v2", Long.class);
|
||||
long v3 = SipHashTestUtils.getPrivateField(sipHash, "v3", Long.class);
|
||||
|
||||
Assert.assertEquals(v0, 4925064773550298181L);
|
||||
Assert.assertEquals(v1, 2461839666708829781L);
|
||||
Assert.assertEquals(v2, 6579568090023412561L);
|
||||
Assert.assertEquals(v3, 3611922228250500171L);
|
||||
|
||||
int c = SipHashTestUtils.getPrivateField(sipHash, "c", Integer.class);
|
||||
int d = SipHashTestUtils.getPrivateField(sipHash, "d", Integer.class);
|
||||
|
||||
Assert.assertEquals(c, 4);
|
||||
Assert.assertEquals(d, 8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initializeStateWithKeyThenHash() throws Exception {
|
||||
SipHash sipHash = new SipHash("0123456789ABCDEF".getBytes());
|
||||
|
||||
long v0 = SipHashTestUtils.getPrivateField(sipHash, "v0", Long.class);
|
||||
long v1 = SipHashTestUtils.getPrivateField(sipHash, "v1", Long.class);
|
||||
long v2 = SipHashTestUtils.getPrivateField(sipHash, "v2", Long.class);
|
||||
long v3 = SipHashTestUtils.getPrivateField(sipHash, "v3", Long.class);
|
||||
|
||||
Assert.assertEquals(v0, 4925064773550298181L);
|
||||
Assert.assertEquals(v1, 2461839666708829781L);
|
||||
Assert.assertEquals(v2, 6579568090023412561L);
|
||||
Assert.assertEquals(v3, 3611922228250500171L);
|
||||
|
||||
int c = SipHashTestUtils.getPrivateField(sipHash, "c", Integer.class);
|
||||
int d = SipHashTestUtils.getPrivateField(sipHash, "d", Integer.class);
|
||||
|
||||
Assert.assertEquals(c, SipHashConstants.DEFAULT_C);
|
||||
Assert.assertEquals(d, SipHashConstants.DEFAULT_D);
|
||||
|
||||
SipHashResult hashResult = sipHash.hash("zymotechnics".getBytes());
|
||||
|
||||
Assert.assertEquals(hashResult.get(), 699588702094987020L);
|
||||
Assert.assertEquals(hashResult.getHex(), "9b57037cd3f8f0c");
|
||||
Assert.assertEquals(hashResult.getHex(true), "09b57037cd3f8f0c");
|
||||
Assert.assertEquals(hashResult.getHex(SipHashCase.UPPER), "9B57037CD3F8F0C");
|
||||
Assert.assertEquals(hashResult.getHex(true, SipHashCase.UPPER), "09B57037CD3F8F0C");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initializeStateWithKeyAndCDThenHash() throws Exception {
|
||||
SipHash sipHash = new SipHash("0123456789ABCDEF".getBytes(), 4, 8);
|
||||
|
||||
long v0 = SipHashTestUtils.getPrivateField(sipHash, "v0", Long.class);
|
||||
long v1 = SipHashTestUtils.getPrivateField(sipHash, "v1", Long.class);
|
||||
long v2 = SipHashTestUtils.getPrivateField(sipHash, "v2", Long.class);
|
||||
long v3 = SipHashTestUtils.getPrivateField(sipHash, "v3", Long.class);
|
||||
|
||||
Assert.assertEquals(v0, 4925064773550298181L);
|
||||
Assert.assertEquals(v1, 2461839666708829781L);
|
||||
Assert.assertEquals(v2, 6579568090023412561L);
|
||||
Assert.assertEquals(v3, 3611922228250500171L);
|
||||
|
||||
int c = SipHashTestUtils.getPrivateField(sipHash, "c", Integer.class);
|
||||
int d = SipHashTestUtils.getPrivateField(sipHash, "d", Integer.class);
|
||||
|
||||
Assert.assertEquals(c, 4);
|
||||
Assert.assertEquals(d, 8);
|
||||
|
||||
SipHashResult hashResult = sipHash.hash("zymotechnics".getBytes());
|
||||
|
||||
Assert.assertEquals(hashResult.get(), -3891084581787974112L); // overflow
|
||||
Assert.assertEquals(hashResult.getHex(), "ca0017304f874620");
|
||||
Assert.assertEquals(hashResult.getHex(true), "ca0017304f874620");
|
||||
Assert.assertEquals(hashResult.getHex(SipHashCase.UPPER), "CA0017304F874620");
|
||||
Assert.assertEquals(hashResult.getHex(true, SipHashCase.UPPER), "CA0017304F874620");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.zackehh.siphash;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class SipHashTestUtils {
|
||||
|
||||
static <T> T getPrivateField(Object obj, String name, Class<T> clazz) throws Exception {
|
||||
Field f = obj.getClass().getDeclaredField(name);
|
||||
f.setAccessible(true);
|
||||
return clazz.cast(f.get(obj));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import java.awt.Toolkit;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.io.File;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -22,10 +24,13 @@ import com.gpl.rpg.atcontentstudio.ui.WorkspaceSelector;
|
||||
public class ATContentStudio {
|
||||
|
||||
public static final String APP_NAME = "Andor's Trail Content Studio";
|
||||
public static final String APP_VERSION = "v0.5.2";
|
||||
public static final String APP_VERSION = "v0.6.1";
|
||||
|
||||
public static boolean STARTED = false;
|
||||
public static StudioFrame frame = null;
|
||||
|
||||
//Need to keep a strong reference to it, to avoid garbage collection that'll reset these loggers.
|
||||
public static final List<Logger> configuredLoggers = new LinkedList<Logger>();
|
||||
|
||||
/**
|
||||
* @param args
|
||||
@@ -34,8 +39,6 @@ public class ATContentStudio {
|
||||
|
||||
ConfigCache.init();
|
||||
|
||||
Logger.getLogger(ExpressionParser.class.getName()).setLevel(Level.OFF);
|
||||
|
||||
try {
|
||||
String laf = ConfigCache.getFavoriteLaFClassName();
|
||||
if (laf == null) laf = UIManager.getSystemLookAndFeelClassName();
|
||||
@@ -50,6 +53,10 @@ public class ATContentStudio {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
//Need to keep a strong reference to it, to avoid garbage collection that'll reset this setting.
|
||||
Logger l = Logger.getLogger(ExpressionParser.class.getName());
|
||||
l.setLevel(Level.OFF);
|
||||
configuredLoggers.add(l);
|
||||
|
||||
final WorkspaceSelector wsSelect = new WorkspaceSelector();
|
||||
wsSelect.pack();
|
||||
@@ -69,7 +76,7 @@ public class ATContentStudio {
|
||||
Workspace.setActive(workspaceRoot);
|
||||
frame = new StudioFrame(APP_NAME+" "+APP_VERSION);
|
||||
frame.setVisible(true);
|
||||
frame.setDefaultCloseOperation(StudioFrame.EXIT_ON_CLOSE);
|
||||
frame.setDefaultCloseOperation(StudioFrame.DO_NOTHING_ON_CLOSE);
|
||||
};
|
||||
});
|
||||
for (File f : ConfigCache.getKnownWorkspaces()) {
|
||||
|
||||
|
Before Width: | Height: | Size: 566 B |
|
Before Width: | Height: | Size: 278 B |
BIN
src/com/gpl/rpg/atcontentstudio/img/status_blue.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
src/com/gpl/rpg/atcontentstudio/img/status_green.png
Normal file
|
After Width: | Height: | Size: 1013 B |
BIN
src/com/gpl/rpg/atcontentstudio/img/status_orange.png
Normal file
|
After Width: | Height: | Size: 1023 B |
BIN
src/com/gpl/rpg/atcontentstudio/img/status_red.png
Normal file
|
After Width: | Height: | Size: 880 B |
BIN
src/com/gpl/rpg/atcontentstudio/img/status_unknown.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 481 B |
@@ -116,4 +116,9 @@ public class ClosedProject implements ProjectTreeNode {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsSaving() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -176,6 +176,10 @@ public abstract class GameDataElement implements ProjectTreeNode, Serializable {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean needsSaving() {
|
||||
return this.state == State.modified || this.state == State.created;
|
||||
}
|
||||
|
||||
public abstract String getProjectFilename();
|
||||
|
||||
public abstract void save();
|
||||
|
||||
@@ -213,11 +213,11 @@ public class GameSource implements ProjectTreeNode, Serializable {
|
||||
@Override
|
||||
public String getDesc() {
|
||||
switch(type) {
|
||||
case altered: return "Altered data";
|
||||
case created: return "Created data";
|
||||
case referenced: return "Referenced data";
|
||||
case source: return "AT Source"; //The fact that it is from "source" is already mentionned by its parent.
|
||||
default: return "Game data";
|
||||
case altered: return (needsSaving() ? "*" : "")+"Altered data";
|
||||
case created: return (needsSaving() ? "*" : "")+"Created data";
|
||||
case referenced: return (needsSaving() ? "*" : "")+"Referenced data";
|
||||
case source: return (needsSaving() ? "*" : "")+"AT Source"; //The fact that it is from "source" is already mentionned by its parent.
|
||||
default: return (needsSaving() ? "*" : "")+"Game data";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,4 +283,12 @@ public class GameSource implements ProjectTreeNode, Serializable {
|
||||
public WorldmapSegment getWorldmapSegment(String id) {
|
||||
return worldmap.getWorldmapSegment(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsSaving() {
|
||||
for (ProjectTreeNode node : v.getNonEmptyIterable()) {
|
||||
if (node.needsSaving()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import com.gpl.rpg.atcontentstudio.model.gamedata.ItemCategory;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.JSONElement;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.NPC;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.Quest;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.QuestStage;
|
||||
import com.gpl.rpg.atcontentstudio.model.maps.TMXMap;
|
||||
import com.gpl.rpg.atcontentstudio.model.maps.TMXMapSet;
|
||||
import com.gpl.rpg.atcontentstudio.model.maps.Worldmap;
|
||||
@@ -191,7 +192,7 @@ public class Project implements ProjectTreeNode, Serializable {
|
||||
}
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return name;
|
||||
return (needsSaving() ? "*" : "")+name;
|
||||
}
|
||||
|
||||
|
||||
@@ -265,7 +266,7 @@ public class Project implements ProjectTreeNode, Serializable {
|
||||
}
|
||||
}
|
||||
for (ProjectTreeNode node : baseContent.gameMaps.tmxMaps) {
|
||||
((TMXMap)node).parse();
|
||||
((TMXMap)node).link();
|
||||
}
|
||||
for (ProjectTreeNode node : alteredContent.gameData.v.getNonEmptyIterable()) {
|
||||
if (node instanceof GameDataCategory<?>) {
|
||||
@@ -275,7 +276,7 @@ public class Project implements ProjectTreeNode, Serializable {
|
||||
}
|
||||
}
|
||||
for (ProjectTreeNode node : alteredContent.gameMaps.tmxMaps) {
|
||||
((TMXMap)node).parse();
|
||||
((TMXMap)node).link();
|
||||
}
|
||||
for (ProjectTreeNode node : createdContent.gameData.v.getNonEmptyIterable()) {
|
||||
if (node instanceof GameDataCategory<?>) {
|
||||
@@ -285,9 +286,6 @@ public class Project implements ProjectTreeNode, Serializable {
|
||||
}
|
||||
}
|
||||
for (ProjectTreeNode node : createdContent.gameMaps.tmxMaps) {
|
||||
((TMXMap)node).parse();
|
||||
}
|
||||
for (ProjectTreeNode node : baseContent.gameMaps.tmxMaps) {
|
||||
((TMXMap)node).link();
|
||||
}
|
||||
|
||||
@@ -665,6 +663,26 @@ public class Project implements ProjectTreeNode, Serializable {
|
||||
return sheet;
|
||||
}
|
||||
|
||||
public int getSpritesheetCount() {
|
||||
return createdContent.gameSprites.spritesheets.size() + baseContent.gameSprites.spritesheets.size();
|
||||
}
|
||||
|
||||
public Spritesheet getSpritesheet(int index) {
|
||||
if (index < createdContent.gameSprites.spritesheets.size()) {
|
||||
return createdContent.gameSprites.spritesheets.get(index);
|
||||
} else if (index < getQuestCount()){
|
||||
return getSpritesheet(baseContent.gameSprites.spritesheets.get(index - createdContent.gameSprites.spritesheets.size()).id);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getSpritesheetIndex(Spritesheet spritesheet) {
|
||||
if (spritesheet.getDataType() == GameSource.Type.created) {
|
||||
return createdContent.gameSprites.spritesheets.indexOf(spritesheet);
|
||||
} else {
|
||||
return createdContent.gameSprites.spritesheets.size() + baseContent.gameSprites.spritesheets.indexOf(baseContent.gameSprites.getSpritesheet(spritesheet.id));
|
||||
}
|
||||
}
|
||||
|
||||
public TMXMap getMap(String id) {
|
||||
TMXMap map = createdContent.gameMaps.getMap(id);
|
||||
@@ -741,6 +759,15 @@ public class Project implements ProjectTreeNode, Serializable {
|
||||
} else {
|
||||
if (type == GameSource.Type.source) {
|
||||
JSONElement clone = (JSONElement) node.clone();
|
||||
if (node instanceof Quest) {
|
||||
for (QuestStage oldStage : ((Quest) node).stages) {
|
||||
QuestStage newStage = ((Quest) clone).getStage(oldStage.progress);
|
||||
for (GameDataElement backlink : oldStage.getBacklinks()) {
|
||||
backlink.elementChanged(oldStage, newStage);
|
||||
}
|
||||
oldStage.getBacklinks().clear();
|
||||
}
|
||||
}
|
||||
for (GameDataElement backlink : node.getBacklinks()) {
|
||||
backlink.elementChanged(node, clone);
|
||||
}
|
||||
@@ -1111,6 +1138,13 @@ public class Project implements ProjectTreeNode, Serializable {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean needsSaving() {
|
||||
for (ProjectTreeNode node : v.getNonEmptyIterable()) {
|
||||
if (node.needsSaving()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -53,5 +53,7 @@ public interface ProjectTreeNode extends TreeNode {
|
||||
public GameSource.Type getDataType();
|
||||
|
||||
public boolean isEmpty();
|
||||
|
||||
public boolean needsSaving();
|
||||
|
||||
}
|
||||
|
||||
@@ -140,8 +140,15 @@ public class Workspace implements ProjectTreeNode, Serializable {
|
||||
@Override
|
||||
public void childrenChanged(List<ProjectTreeNode> path) {
|
||||
path.add(0, this);
|
||||
if (projectsTreeModel != null) projectsTreeModel.changeNode(new TreePath(path.toArray()));
|
||||
ATContentStudio.frame.editorChanged(path.get(path.size() - 1));
|
||||
ProjectTreeNode last = path.get(path.size() - 1);
|
||||
if (projectsTreeModel != null) {
|
||||
while (path.size() > 1) {
|
||||
projectsTreeModel.changeNode(new TreePath(path.toArray()));
|
||||
path.remove(path.size()-1);
|
||||
}
|
||||
|
||||
}
|
||||
ATContentStudio.frame.editorChanged(last);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -346,5 +353,14 @@ public class Workspace implements ProjectTreeNode, Serializable {
|
||||
public boolean isEmpty() {
|
||||
return projects.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean needsSaving() {
|
||||
for (ProjectTreeNode node : projects) {
|
||||
if (node.needsSaving()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.gpl.rpg.atcontentstudio.model;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
@@ -13,9 +12,7 @@ import java.util.Map;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.gpl.rpg.atcontentstudio.ATContentStudio;
|
||||
import com.gpl.rpg.atcontentstudio.Notification;
|
||||
import com.gpl.rpg.atcontentstudio.io.JsonPrettyWriter;
|
||||
|
||||
@@ -41,6 +38,12 @@ public class WorkspaceSettings {
|
||||
public static String DEFAULT_IMG_EDITOR_COMMAND = "gimp";
|
||||
public Setting<String> imageEditorCommand = new PrimitiveSetting<String>("imageEditorCommand", DEFAULT_IMG_EDITOR_COMMAND);
|
||||
|
||||
public static String[] LANGUAGE_LIST = new String[]{null, "de", "ru", "pl", "fr", "it", "es", "nl", "uk", "ca", "sv", "pt", "pt_BR", "zh_Hant", "zh_Hans", "ja", "cs", "tr", "ko", "hu", "sl", "bg", "id", "fi", "th", "gl", "ms" ,"pa", "az"};
|
||||
public Setting<String> translatorLanguage = new NullDefaultPrimitiveSetting<String>("translatorLanguage");
|
||||
public static Boolean DEFAULT_ALLOW_INTERNET = true;
|
||||
public Setting<Boolean> useInternet = new PrimitiveSetting<Boolean>("useInternet", DEFAULT_ALLOW_INTERNET);
|
||||
|
||||
|
||||
public List<Setting<? extends Object>> settings = new ArrayList<Setting<? extends Object>>();
|
||||
|
||||
public WorkspaceSettings(Workspace parent) {
|
||||
@@ -50,6 +53,8 @@ public class WorkspaceSettings {
|
||||
settings.add(useSystemDefaultImageViewer);
|
||||
settings.add(useSystemDefaultImageEditor);
|
||||
settings.add(imageEditorCommand);
|
||||
settings.add(translatorLanguage);
|
||||
settings.add(useInternet);
|
||||
file = new File(parent.baseFolder, FILENAME);
|
||||
if (file.exists()) {
|
||||
load(file);
|
||||
@@ -63,20 +68,14 @@ public class WorkspaceSettings {
|
||||
reader = new FileReader(f);
|
||||
@SuppressWarnings("rawtypes")
|
||||
Map jsonSettings = (Map) parser.parse(reader);
|
||||
Integer version = (Integer) jsonSettings.get(VERSION_KEY);
|
||||
Integer version = ((Number) jsonSettings.get(VERSION_KEY)).intValue();
|
||||
if (version != null) {
|
||||
if (version >= 1) {
|
||||
loadv1(jsonSettings);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
Notification.addError("Error while parsing workspace settings: "+e.getMessage());
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
Notification.addError("Error while parsing workspace settings: "+e.getMessage());
|
||||
e.printStackTrace();
|
||||
} catch (ParseException e) {
|
||||
} catch (Exception e) {
|
||||
Notification.addError("Error while parsing workspace settings: "+e.getMessage());
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
@@ -110,7 +109,7 @@ public class WorkspaceSettings {
|
||||
return;
|
||||
}
|
||||
|
||||
json.put(VERSION_KEY, ATContentStudio.APP_VERSION);
|
||||
json.put(VERSION_KEY, SETTINGS_VERSION);
|
||||
StringWriter writer = new JsonPrettyWriter();
|
||||
try {
|
||||
JSONObject.writeJSONString(json, writer);
|
||||
@@ -180,6 +179,18 @@ public class WorkspaceSettings {
|
||||
|
||||
}
|
||||
|
||||
public class NullDefaultPrimitiveSetting<X extends Object> extends PrimitiveSetting<X> {
|
||||
|
||||
public NullDefaultPrimitiveSetting(String id) {
|
||||
super(id, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveToJson(Map json) {
|
||||
if (value != null) json.put(id, value);
|
||||
}
|
||||
}
|
||||
|
||||
public class ListSetting<X extends Object> extends Setting<List<X>> {
|
||||
|
||||
public ListSetting(String id, List<X> defaultValue) {
|
||||
|
||||
@@ -20,6 +20,8 @@ import com.gpl.rpg.atcontentstudio.model.GameSource;
|
||||
public class ActorCondition extends JSONElement {
|
||||
|
||||
private static final long serialVersionUID = -3969824899972048507L;
|
||||
|
||||
public static final Integer CLEAR_AC_MAGNITUDE = -99;
|
||||
|
||||
// Available from init state
|
||||
//public String id; inherited.
|
||||
@@ -86,7 +88,7 @@ public class ActorCondition extends JSONElement {
|
||||
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return ((this.state == State.modified || this.state == State.created) ? "*" : "")+display_name+" ("+id+")";
|
||||
return (needsSaving() ? "*" : "")+display_name+" ("+id+")";
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
|
||||
@@ -18,7 +18,6 @@ import com.gpl.rpg.atcontentstudio.Notification;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameDataElement;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameSource;
|
||||
import com.gpl.rpg.atcontentstudio.model.Project;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameDataElement.State;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.Requirement.RequirementType;
|
||||
import com.gpl.rpg.atcontentstudio.model.maps.TMXMap;
|
||||
import com.gpl.rpg.atcontentstudio.ui.DefaultIcons;
|
||||
@@ -92,7 +91,7 @@ public class Dialogue extends JSONElement {
|
||||
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return ((this.state == State.modified || this.state == State.created) ? "*" : "")+id;
|
||||
return (needsSaving() ? "*" : "")+id;
|
||||
}
|
||||
|
||||
public static String getStaticDesc() {
|
||||
@@ -266,6 +265,12 @@ public class Dialogue extends JSONElement {
|
||||
break;
|
||||
case questProgress:
|
||||
reward.reward_obj = proj.getQuest(reward.reward_obj_id);
|
||||
if (reward.reward_obj != null && reward.reward_value != null) {
|
||||
QuestStage stage = ((Quest)reward.reward_obj).getStage(reward.reward_value);
|
||||
if (stage != null) {
|
||||
stage.addBacklink(this);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case skillIncrease:
|
||||
//Nothing to do (yet ?).
|
||||
@@ -362,11 +367,7 @@ public class Dialogue extends JSONElement {
|
||||
}
|
||||
if (r.requirements != null) {
|
||||
for (Requirement req : r.requirements) {
|
||||
if (req.required_obj == oldOne) {
|
||||
oldOne.removeBacklink(this);
|
||||
req.required_obj = newOne;
|
||||
if (newOne != null) newOne.addBacklink(this);
|
||||
}
|
||||
req.elementChanged(oldOne, newOne);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -378,6 +379,12 @@ public class Dialogue extends JSONElement {
|
||||
r.reward_obj = newOne;
|
||||
if (newOne != null) newOne.addBacklink(this);
|
||||
}
|
||||
if (oldOne instanceof QuestStage) {
|
||||
if (r.reward_obj != null && r.reward_obj.equals(oldOne.parent) && r.reward_value != null && r.reward_value.equals(((QuestStage) oldOne).progress)) {
|
||||
oldOne.removeBacklink((GameDataElement) this);
|
||||
if (newOne != null) newOne.addBacklink((GameDataElement) this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import com.gpl.rpg.atcontentstudio.Notification;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameDataElement;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameSource;
|
||||
import com.gpl.rpg.atcontentstudio.model.Project;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameDataElement.State;
|
||||
import com.gpl.rpg.atcontentstudio.ui.DefaultIcons;
|
||||
|
||||
|
||||
@@ -47,7 +46,7 @@ public class Droplist extends JSONElement {
|
||||
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return ((this.state == State.modified || this.state == State.created) ? "*" : "")+id;
|
||||
return (needsSaving() ? "*" : "")+id;
|
||||
}
|
||||
|
||||
public static String getStaticDesc() {
|
||||
|
||||
@@ -102,7 +102,7 @@ public class GameDataCategory<E extends JSONElement> extends ArrayList<E> implem
|
||||
}
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return this.name;
|
||||
return (needsSaving() ? "*" : "")+this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -208,7 +208,7 @@ public class GameDataCategory<E extends JSONElement> extends ArrayList<E> implem
|
||||
impactedCategory = getProject().createdContent.gameData.getCategory(node.getClass());
|
||||
impactedFileName = node.getProjectFilename();
|
||||
}
|
||||
} else if (node.state == GameDataElement.State.modified) {
|
||||
} else if (node.needsSaving()) {
|
||||
events.add(new SaveEvent(SaveEvent.Type.alsoSave, node));
|
||||
}
|
||||
if (containedIds.containsKey(node.id)) {
|
||||
@@ -242,5 +242,12 @@ public class GameDataCategory<E extends JSONElement> extends ArrayList<E> implem
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsSaving() {
|
||||
for (E node : this) {
|
||||
if (node.needsSaving()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@ public class GameDataSet implements ProjectTreeNode, Serializable {
|
||||
}
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return "JSON data";
|
||||
return (needsSaving() ? "*" : "")+"JSON data";
|
||||
}
|
||||
|
||||
|
||||
@@ -463,4 +463,11 @@ public class GameDataSet implements ProjectTreeNode, Serializable {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsSaving() {
|
||||
for (ProjectTreeNode node : v.getNonEmptyIterable()) {
|
||||
if (node.needsSaving()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import com.gpl.rpg.atcontentstudio.Notification;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameDataElement;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameSource;
|
||||
import com.gpl.rpg.atcontentstudio.model.Project;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameDataElement.State;
|
||||
|
||||
public class Item extends JSONElement {
|
||||
|
||||
@@ -102,7 +101,7 @@ public class Item extends JSONElement {
|
||||
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return ((this.state == State.modified || this.state == State.created) ? "*" : "")+name+" ("+id+")";
|
||||
return (needsSaving() ? "*" : "")+name+" ("+id+")";
|
||||
}
|
||||
|
||||
public static String getStaticDesc() {
|
||||
|
||||
@@ -17,7 +17,6 @@ import org.json.simple.parser.ParseException;
|
||||
import com.gpl.rpg.atcontentstudio.Notification;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameDataElement;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameSource;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameDataElement.State;
|
||||
|
||||
public class ItemCategory extends JSONElement {
|
||||
|
||||
@@ -100,7 +99,7 @@ public class ItemCategory extends JSONElement {
|
||||
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return ((this.state == State.modified || this.state == State.created) ? "*" : "")+name+" ("+id+")";
|
||||
return (needsSaving() ? "*" : "")+name+" ("+id+")";
|
||||
}
|
||||
|
||||
public static String getStaticDesc() {
|
||||
|
||||
@@ -17,7 +17,6 @@ import com.gpl.rpg.atcontentstudio.Notification;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameDataElement;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameSource;
|
||||
import com.gpl.rpg.atcontentstudio.model.Project;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameDataElement.State;
|
||||
|
||||
public class NPC extends JSONElement {
|
||||
|
||||
@@ -96,7 +95,7 @@ public class NPC extends JSONElement {
|
||||
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return ((this.state == State.modified || this.state == State.created) ? "*" : "")+name+" ("+id+")";
|
||||
return (needsSaving() ? "*" : "")+name+" ("+id+")";
|
||||
}
|
||||
|
||||
public static String getStaticDesc() {
|
||||
|
||||
@@ -16,7 +16,6 @@ import org.json.simple.parser.ParseException;
|
||||
import com.gpl.rpg.atcontentstudio.Notification;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameDataElement;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameSource;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameDataElement.State;
|
||||
import com.gpl.rpg.atcontentstudio.ui.DefaultIcons;
|
||||
|
||||
public class Quest extends JSONElement {
|
||||
@@ -31,25 +30,9 @@ public class Quest extends JSONElement {
|
||||
public Integer visible_in_log = null;
|
||||
public List<QuestStage> stages = null;
|
||||
|
||||
public static class QuestStage implements Cloneable {
|
||||
public Integer progress = null;
|
||||
public String log_text = null;
|
||||
public Integer exp_reward = null;
|
||||
public Integer finishes_quest = null;
|
||||
|
||||
public Object clone() {
|
||||
try {
|
||||
return super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return ((this.state == State.modified || this.state == State.created) ? "*" : "")+name+" ("+id+")";
|
||||
return (needsSaving() ? "*" : "")+name+" ("+id+")";
|
||||
}
|
||||
|
||||
public static String getStaticDesc() {
|
||||
@@ -106,6 +89,8 @@ public class Quest extends JSONElement {
|
||||
Quest quest = new Quest();
|
||||
quest.id = (String) questJson.get("id");
|
||||
quest.name = (String) questJson.get("name");
|
||||
//Quests have to be parsed to have their stages initialized.
|
||||
quest.parse(questJson);
|
||||
return quest;
|
||||
}
|
||||
|
||||
@@ -118,15 +103,12 @@ public class Quest extends JSONElement {
|
||||
this.stages = new ArrayList<QuestStage>();
|
||||
for (Object questStageJsonObj : questStagesJson) {
|
||||
Map questStageJson = (Map)questStageJsonObj;
|
||||
QuestStage questStage = new QuestStage();
|
||||
questStage.progress = JSONElement.getInteger((Number) questStageJson.get("progress"));
|
||||
questStage.log_text = (String) questStageJson.get("logText");
|
||||
questStage.exp_reward = JSONElement.getInteger((Number) questStageJson.get("rewardExperience"));
|
||||
questStage.finishes_quest = JSONElement.getInteger((Number) questStageJson.get("finishesQuest"));
|
||||
QuestStage questStage = new QuestStage(this);
|
||||
questStage.parse(questStageJson);
|
||||
this.stages.add(questStage);
|
||||
}
|
||||
}
|
||||
|
||||
this.state = State.parsed;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -143,6 +125,9 @@ public class Quest extends JSONElement {
|
||||
return;
|
||||
}
|
||||
|
||||
for (QuestStage stage : stages) {
|
||||
stage.link();
|
||||
}
|
||||
//Nothing to link to :D
|
||||
this.state = State.linked;
|
||||
}
|
||||
@@ -165,9 +150,9 @@ public class Quest extends JSONElement {
|
||||
clone.name = this.name;
|
||||
clone.visible_in_log = this.visible_in_log;
|
||||
if (this.stages != null) {
|
||||
clone.stages = new ArrayList<Quest.QuestStage>();
|
||||
clone.stages = new ArrayList<QuestStage>();
|
||||
for (QuestStage stage : this.stages){
|
||||
clone.stages.add((QuestStage) stage.clone());
|
||||
clone.stages.add((QuestStage) stage.clone(clone));
|
||||
}
|
||||
}
|
||||
return clone;
|
||||
@@ -189,12 +174,7 @@ public class Quest extends JSONElement {
|
||||
List stagesJson = new ArrayList();
|
||||
questJson.put("stages", stagesJson);
|
||||
for (QuestStage stage : this.stages) {
|
||||
Map stageJson = new LinkedHashMap();
|
||||
stagesJson.add(stageJson);
|
||||
if (stage.progress != null) stageJson.put("progress", stage.progress);
|
||||
if (stage.log_text != null) stageJson.put("logText", stage.log_text);
|
||||
if (stage.exp_reward != null) stageJson.put("rewardExperience", stage.exp_reward);
|
||||
if (stage.finishes_quest != null) stageJson.put("finishesQuest", stage.finishes_quest);
|
||||
stagesJson.add(stage.toJson());
|
||||
}
|
||||
}
|
||||
return questJson;
|
||||
@@ -205,5 +185,14 @@ public class Quest extends JSONElement {
|
||||
public String getProjectFilename() {
|
||||
return "questlist_"+getProject().name+".json";
|
||||
}
|
||||
|
||||
public QuestStage getStage(Integer stageId) {
|
||||
for (QuestStage stage : stages) {
|
||||
if (stage.progress.equals(stageId)) {
|
||||
return stage;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
103
src/com/gpl/rpg/atcontentstudio/model/gamedata/QuestStage.java
Normal file
@@ -0,0 +1,103 @@
|
||||
package com.gpl.rpg.atcontentstudio.model.gamedata;
|
||||
|
||||
import java.awt.Image;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.gpl.rpg.atcontentstudio.model.GameDataElement;
|
||||
import com.gpl.rpg.atcontentstudio.ui.DefaultIcons;
|
||||
|
||||
public class QuestStage extends JSONElement {
|
||||
|
||||
private static final long serialVersionUID = 8313645819951513431L;
|
||||
|
||||
public Integer progress = null;
|
||||
public String log_text = null;
|
||||
public Integer exp_reward = null;
|
||||
public Integer finishes_quest = null;
|
||||
|
||||
public QuestStage(Quest parent){
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public QuestStage clone(Quest cloneParent) {
|
||||
QuestStage clone = new QuestStage(cloneParent);
|
||||
clone.progress = progress != null ? new Integer(progress) : null;
|
||||
clone.log_text = log_text != null ? new String(log_text) : null;
|
||||
clone.exp_reward = exp_reward != null ? new Integer(exp_reward) : null;
|
||||
clone.finishes_quest = finishes_quest != null ? new Integer(finishes_quest) : null;
|
||||
clone.id = this.id;
|
||||
return clone;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public void parse(Map jsonObj) {
|
||||
progress = JSONElement.getInteger((Number) jsonObj.get("progress"));
|
||||
this.id = ((Quest)parent).id+":"+progress;
|
||||
log_text = (String) jsonObj.get("logText");
|
||||
exp_reward = JSONElement.getInteger((Number) jsonObj.get("rewardExperience"));
|
||||
finishes_quest = JSONElement.getInteger((Number) jsonObj.get("finishesQuest"));
|
||||
state = State.parsed;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@Override
|
||||
public Map toJson() {
|
||||
Map stageJson = new LinkedHashMap();
|
||||
if (progress != null) stageJson.put("progress", progress);
|
||||
if (log_text != null) stageJson.put("logText", log_text);
|
||||
if (exp_reward != null) stageJson.put("rewardExperience", exp_reward);
|
||||
if (finishes_quest != null) stageJson.put("finishesQuest", finishes_quest);
|
||||
return stageJson;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return progress+" - "+(exp_reward != null ? "["+exp_reward+"XP]" : "")+((finishes_quest != null) && (finishes_quest == 1) ? "[END]" : "")+log_text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void link() {
|
||||
if (this.state == State.created || this.state == State.modified || this.state == State.saved) {
|
||||
//This type of state is unrelated to parsing/linking.
|
||||
return;
|
||||
}
|
||||
if (this.state == State.init) {
|
||||
//Not parsed yet.
|
||||
this.parse();
|
||||
} else if (this.state == State.linked) {
|
||||
//Already linked.
|
||||
return;
|
||||
}
|
||||
|
||||
//Nothing to link to :D
|
||||
this.state = State.linked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void elementChanged(GameDataElement oldOne, GameDataElement newOne) {
|
||||
// Nothing to link to.
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProjectFilename() {
|
||||
return ((Quest)parent).getProjectFilename();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameDataElement clone() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image getIcon() {
|
||||
return DefaultIcons.getQuestIcon();
|
||||
}
|
||||
|
||||
public Image getImage() {
|
||||
return DefaultIcons.getQuestImage();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -112,6 +112,12 @@ public class Requirement extends JSONElement {
|
||||
case questLatestProgress:
|
||||
case questProgress:
|
||||
this.required_obj = proj.getQuest(required_obj_id);
|
||||
if (this.required_obj != null && this.required_value != null) {
|
||||
QuestStage stage = ((Quest)this.required_obj).getStage(this.required_value);
|
||||
if (stage != null) {
|
||||
stage.addBacklink((GameDataElement) this.parent);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case consumedBonemeals:
|
||||
case skillLevel:
|
||||
@@ -120,6 +126,7 @@ public class Requirement extends JSONElement {
|
||||
break;
|
||||
}
|
||||
if (this.required_obj != null) this.required_obj.addBacklink((GameDataElement) this.parent);
|
||||
this.state = State.linked;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -149,6 +156,12 @@ public class Requirement extends JSONElement {
|
||||
this.required_obj = newOne;
|
||||
if (newOne != null) newOne.addBacklink((GameDataElement) this.parent);
|
||||
}
|
||||
if (oldOne instanceof QuestStage) {
|
||||
if (this.required_obj != null && this.required_obj.equals(oldOne.parent) && this.required_value != null && this.required_value.equals(((QuestStage) oldOne).progress)) {
|
||||
oldOne.removeBacklink((GameDataElement) this.parent);
|
||||
if (newOne != null) newOne.addBacklink((GameDataElement) this.parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public String getProjectFilename() {
|
||||
|
||||
@@ -62,7 +62,6 @@ public class TMXMap extends GameDataElement {
|
||||
public Integer outside = null;
|
||||
public ColorFilter colorFilter = null;
|
||||
|
||||
public boolean writable = false;
|
||||
public boolean changedOnDisk = false;
|
||||
public int dismissNextChangeNotif = 0;
|
||||
|
||||
@@ -129,6 +128,7 @@ public class TMXMap extends GameDataElement {
|
||||
clone.groups = new ArrayList<MapObjectGroup>();
|
||||
}
|
||||
MapObjectGroup group = new MapObjectGroup((tiled.core.ObjectGroup) layer, this);
|
||||
group.link();
|
||||
clone.groups.add(group);
|
||||
}
|
||||
}
|
||||
@@ -201,7 +201,7 @@ public class TMXMap extends GameDataElement {
|
||||
}
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return ((this.state == State.modified || this.state == State.created) ? "*" : "")+id;
|
||||
return (needsSaving() ? "*" : "")+id;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -315,7 +315,7 @@ public class TMXMap extends GameDataElement {
|
||||
if (this.state == GameDataElement.State.init) {
|
||||
parse();
|
||||
}
|
||||
if (this.state == GameDataElement.State.parsed || this.state == GameDataElement.State.created) {
|
||||
if (this.state == GameDataElement.State.parsed) {
|
||||
if (groups != null) {
|
||||
for (MapObjectGroup group : groups) {
|
||||
group.link();
|
||||
|
||||
@@ -4,7 +4,6 @@ import java.awt.Image;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardWatchEventKinds;
|
||||
@@ -26,7 +25,6 @@ import com.gpl.rpg.atcontentstudio.model.Project;
|
||||
import com.gpl.rpg.atcontentstudio.model.Project.ResourceSet;
|
||||
import com.gpl.rpg.atcontentstudio.model.ProjectTreeNode;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.GameDataSet;
|
||||
import com.gpl.rpg.atcontentstudio.model.sprites.SpriteSheetSet;
|
||||
import com.gpl.rpg.atcontentstudio.ui.DefaultIcons;
|
||||
import com.gpl.rpg.atcontentstudio.utils.FileUtils;
|
||||
|
||||
@@ -187,7 +185,7 @@ public class TMXMapSet implements ProjectTreeNode {
|
||||
}
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return "TMX Maps";
|
||||
return (needsSaving() ? "*" : "")+"TMX Maps";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -262,4 +260,12 @@ public class TMXMapSet implements ProjectTreeNode {
|
||||
return tmxMaps.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsSaving() {
|
||||
for (ProjectTreeNode node : tmxMaps) {
|
||||
if (node.needsSaving()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ public class Worldmap extends ArrayList<WorldmapSegment> implements ProjectTreeN
|
||||
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return "Worldmap";
|
||||
return (needsSaving() ? "*" : "")+"Worldmap";
|
||||
}
|
||||
@Override
|
||||
public void notifyCreated() {
|
||||
@@ -277,6 +277,13 @@ public class Worldmap extends ArrayList<WorldmapSegment> implements ProjectTreeN
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean needsSaving() {
|
||||
for (ProjectTreeNode node : this) {
|
||||
if (node.needsSaving()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public class WorldmapSegment extends GameDataElement {
|
||||
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return id;
|
||||
return (needsSaving() ? "*" : "")+id;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -97,7 +97,7 @@ public class SavedGame extends GameDataElement {
|
||||
}
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return loadedSave.displayInfo;
|
||||
return (needsSaving() ? "*" : "")+loadedSave.displayInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -128,7 +128,7 @@ public class SavedGamesSet implements ProjectTreeNode, Serializable {
|
||||
}
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return "Saved games";
|
||||
return (needsSaving() ? "*" : "")+"Saved games";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -168,4 +168,13 @@ public class SavedGamesSet implements ProjectTreeNode, Serializable {
|
||||
public boolean isEmpty() {
|
||||
return saves.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean needsSaving() {
|
||||
for (ProjectTreeNode node : saves) {
|
||||
if (node.needsSaving()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ public class SpriteSheetSet implements ProjectTreeNode {
|
||||
}
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return "Spritesheets";
|
||||
return (needsSaving() ? "*" : "")+"Spritesheets";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -151,4 +151,12 @@ public class SpriteSheetSet implements ProjectTreeNode {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsSaving() {
|
||||
for (ProjectTreeNode node : spritesheets) {
|
||||
if (node.needsSaving()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ public class Spritesheet extends GameDataElement {
|
||||
}
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return spritesheetFile.getName();
|
||||
return (needsSaving() ? "*" : "")+spritesheetFile.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -413,7 +413,7 @@ public class WriterModeData extends GameDataElement {
|
||||
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return (this.state == State.modified ? "*" : "")+id;
|
||||
return (needsSaving() ? "*" : "")+id;
|
||||
}
|
||||
@Override
|
||||
public Project getProject() {
|
||||
|
||||
@@ -23,7 +23,6 @@ import org.json.simple.parser.ParseException;
|
||||
import com.gpl.rpg.atcontentstudio.Notification;
|
||||
import com.gpl.rpg.atcontentstudio.io.JsonPrettyWriter;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameDataElement;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameDataElement.State;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameSource;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameSource.Type;
|
||||
import com.gpl.rpg.atcontentstudio.model.Project;
|
||||
@@ -110,7 +109,7 @@ public class WriterModeDataSet implements ProjectTreeNode, Serializable {
|
||||
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return "Dialogue sketchs";
|
||||
return (needsSaving() ? "*" : "")+"Dialogue sketchs";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -195,7 +194,7 @@ public class WriterModeDataSet implements ProjectTreeNode, Serializable {
|
||||
public List<SaveEvent> attemptSave() {
|
||||
List<SaveEvent> events = new ArrayList<SaveEvent>();
|
||||
for (WriterModeData data : writerModeDataList) {
|
||||
if (data.state == State.created || data.state == State.modified) {
|
||||
if (data.needsSaving()) {
|
||||
events.add(new SaveEvent(SaveEvent.Type.alsoSave, data));
|
||||
}
|
||||
}
|
||||
@@ -262,5 +261,14 @@ public class WriterModeDataSet implements ProjectTreeNode, Serializable {
|
||||
if (higherEmptyParent != null) higherEmptyParent.notifyCreated();
|
||||
else node.notifyCreated();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean needsSaving() {
|
||||
for (ProjectTreeNode node : writerModeDataList) {
|
||||
if (node.needsSaving()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -72,6 +72,12 @@ public class AboutEditor extends Editor {
|
||||
"<a href=\"http://www.beanshell.org/\">BeanShell</a> by Pat Niemeyer.<br/>" +
|
||||
"License: <a href=\"http://www.beanshell.org/license.html\">LGPL v3</a><br/>" +
|
||||
"<br/>" +
|
||||
"A slightly modified version of <a href=\"https://github.com/zackehh/siphash-java\">SipHash for Java</a> by Isaac Whitfield.<br/>" +
|
||||
"License: <a href=\"https://github.com/zackehh/siphash-java/blob/master/LICENSE\">MIT License</a><br/>" +
|
||||
"<br/>" +
|
||||
"<a href=\"https://jsoup.org/\">jsoup</a> by Jonathan Hedley<br/>" +
|
||||
"License: <a href=\"https://jsoup.org/license\">MIT License</a><br/>" +
|
||||
"<br/>" +
|
||||
"See the tabs below to find the full license text for each of these.<br/>" +
|
||||
"<br/>" +
|
||||
"The Windows installer was created with:<br/>" +
|
||||
@@ -121,6 +127,8 @@ public class AboutEditor extends Editor {
|
||||
editorTabsHolder.add("libtiled-java License", getInfoPane(new Scanner(ATContentStudio.class.getResourceAsStream("/LICENSE.libtiled.txt"), "UTF-8").useDelimiter("\\A").next(), "text/text"));
|
||||
editorTabsHolder.add("prefuse License", getInfoPane(new Scanner(ATContentStudio.class.getResourceAsStream("/license-prefuse.txt"), "UTF-8").useDelimiter("\\A").next(), "text/text"));
|
||||
editorTabsHolder.add("BeanShell License", getInfoPane(new Scanner(ATContentStudio.class.getResourceAsStream("/LICENSE.LGPLv3.txt"), "UTF-8").useDelimiter("\\A").next(), "text/text"));
|
||||
editorTabsHolder.add("SipHash for Java License", getInfoPane(new Scanner(ATContentStudio.class.getResourceAsStream("/LICENSE.siphash-zackehh.txt"), "UTF-8").useDelimiter("\\A").next(), "text/text"));
|
||||
editorTabsHolder.add("jsoup License", getInfoPane(new Scanner(ATContentStudio.class.getResourceAsStream("/LICENSE.jsoup.txt"), "UTF-8").useDelimiter("\\A").next(), "text/text"));
|
||||
editorTabsHolder.add("ATCS License", getInfoPane(new Scanner(ATContentStudio.class.getResourceAsStream("/LICENSE.GPLv3.txt"), "UTF-8").useDelimiter("\\A").next(), "text/text"));
|
||||
|
||||
}
|
||||
|
||||
@@ -231,6 +231,27 @@ public class DefaultIcons {
|
||||
public static Image getZoomImage() { return getImage(ZOOM_RES); }
|
||||
public static Image getZoomIcon() { return getIcon(ZOOM_RES); }
|
||||
|
||||
private static String STATUS_RED_RES = "/com/gpl/rpg/atcontentstudio/img/status_red.png";
|
||||
public static Image getStatusRedImage() { return getImage(STATUS_RED_RES); }
|
||||
public static Image getStatusRedIcon() { return getIcon(STATUS_RED_RES); }
|
||||
|
||||
private static String STATUS_ORANGE_RES = "/com/gpl/rpg/atcontentstudio/img/status_orange.png";
|
||||
public static Image getStatusOrangeImage() { return getImage(STATUS_ORANGE_RES); }
|
||||
public static Image getStatusOrangeIcon() { return getIcon(STATUS_ORANGE_RES); }
|
||||
|
||||
private static String STATUS_GREEN_RES = "/com/gpl/rpg/atcontentstudio/img/status_green.png";
|
||||
public static Image getStatusGreenImage() { return getImage(STATUS_GREEN_RES); }
|
||||
public static Image getStatusGreenIcon() { return getIcon(STATUS_GREEN_RES); }
|
||||
|
||||
private static String STATUS_BLUE_RES = "/com/gpl/rpg/atcontentstudio/img/status_blue.png";
|
||||
public static Image getStatusBlueImage() { return getImage(STATUS_BLUE_RES); }
|
||||
public static Image getStatusBlueIcon() { return getIcon(STATUS_BLUE_RES); }
|
||||
|
||||
private static String STATUS_UNKNOWN_RES = "/com/gpl/rpg/atcontentstudio/img/status_unknown.png";
|
||||
public static Image getStatusUnknownImage() { return getImage(STATUS_UNKNOWN_RES); }
|
||||
public static Image getStatusUnknownIcon() { return getIcon(STATUS_UNKNOWN_RES); }
|
||||
|
||||
|
||||
private static Image getImage(String res) {
|
||||
if (imageCache.get(res) == null) {
|
||||
try {
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.gpl.rpg.atcontentstudio.ui;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Desktop;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.ItemEvent;
|
||||
@@ -10,6 +12,9 @@ import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -34,6 +39,7 @@ import javax.swing.JSpinner.NumberEditor;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.ListModel;
|
||||
import javax.swing.Scrollable;
|
||||
import javax.swing.SpinnerNumberModel;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
@@ -42,12 +48,14 @@ import javax.swing.event.DocumentListener;
|
||||
import javax.swing.event.ListDataEvent;
|
||||
import javax.swing.event.ListDataListener;
|
||||
import javax.swing.text.DefaultFormatter;
|
||||
import javax.swing.text.JTextComponent;
|
||||
|
||||
import com.gpl.rpg.atcontentstudio.ATContentStudio;
|
||||
import com.gpl.rpg.atcontentstudio.Notification;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameDataElement;
|
||||
import com.gpl.rpg.atcontentstudio.model.Project;
|
||||
import com.gpl.rpg.atcontentstudio.model.ProjectElementListener;
|
||||
import com.gpl.rpg.atcontentstudio.model.Workspace;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.ActorCondition;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.Dialogue;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.Droplist;
|
||||
@@ -56,7 +64,9 @@ import com.gpl.rpg.atcontentstudio.model.gamedata.ItemCategory;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.JSONElement;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.NPC;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.Quest;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.QuestStage;
|
||||
import com.gpl.rpg.atcontentstudio.model.maps.TMXMap;
|
||||
import com.gpl.rpg.atcontentstudio.utils.WeblateIntegration;
|
||||
import com.jidesoft.swing.ComboBoxSearchable;
|
||||
import com.jidesoft.swing.JideBoxLayout;
|
||||
|
||||
@@ -108,6 +118,101 @@ public abstract class Editor extends JPanel implements ProjectElementListener {
|
||||
return addTextField(pane, label, value, false, nullListener);
|
||||
}
|
||||
|
||||
public static void addTranslationPane(JPanel pane, final JTextComponent tfComponent, final String initialValue) {if (Workspace.activeWorkspace.settings.translatorLanguage.getCurrentValue() != null) {
|
||||
JPanel labelPane = new JPanel();
|
||||
labelPane.setLayout(new JideBoxLayout(labelPane, JideBoxLayout.LINE_AXIS, 6));
|
||||
final JLabel translateLinkLabel = new JLabel(getWeblateLabelLink(initialValue));
|
||||
labelPane.add(translateLinkLabel, JideBoxLayout.FIX);
|
||||
labelPane.add(new JLabel(" "), JideBoxLayout.FIX);
|
||||
final JLabel translationStatus = new JLabel("Retrieving...");
|
||||
translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusUnknownIcon()));
|
||||
translationStatus.setToolTipText("Connecting to weblate...");
|
||||
labelPane.add(translationStatus, JideBoxLayout.VARY);
|
||||
new Thread() {
|
||||
public void run() {
|
||||
WeblateIntegration.WeblateTranslationUnit unit = WeblateIntegration.getTranslationUnit(initialValue);
|
||||
switch (unit.status) {
|
||||
case absent:
|
||||
translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusRedIcon()));
|
||||
translationStatus.setToolTipText("This string isn't managed by weblate (yet).");
|
||||
break;
|
||||
case done:
|
||||
translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusGreenIcon()));
|
||||
translationStatus.setToolTipText("This string is translated on weblate.");
|
||||
break;
|
||||
case fuzzy:
|
||||
translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusOrangeIcon()));
|
||||
translationStatus.setToolTipText("This string is translated on weblate, but needs a review.");
|
||||
break;
|
||||
case notTranslated:
|
||||
translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusRedIcon()));
|
||||
translationStatus.setToolTipText("This string isn't translated in your language on weblate yet.");
|
||||
break;
|
||||
case warning:
|
||||
translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusOrangeIcon()));
|
||||
translationStatus.setToolTipText("This string is translated on weblate, but triggered some weblate checks.");
|
||||
break;
|
||||
case error:
|
||||
translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusRedIcon()));
|
||||
translationStatus.setToolTipText("Cannot connect to weblate. Check internet connection and firewall settings.");
|
||||
break;
|
||||
case notAllowed:
|
||||
translationStatus.setIcon(new ImageIcon(DefaultIcons.getStatusBlueIcon()));
|
||||
translationStatus.setToolTipText("You have not allowed ATCS to access to internet. You can change this in the workspace settings.");
|
||||
break;
|
||||
}
|
||||
translationStatus.setText(unit.translatedText);
|
||||
};
|
||||
}.start();
|
||||
pane.add(labelPane, JideBoxLayout.FIX);
|
||||
tfComponent.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
translateLinkLabel.setText(getWeblateLabelLink(tfComponent.getText().replaceAll("\n", Matcher.quoteReplacement("\n"))));
|
||||
translateLinkLabel.revalidate();
|
||||
translateLinkLabel.repaint();
|
||||
}
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
translateLinkLabel.setText(getWeblateLabelLink(tfComponent.getText().replaceAll("\n", Matcher.quoteReplacement("\n"))));
|
||||
translateLinkLabel.revalidate();
|
||||
translateLinkLabel.repaint();
|
||||
}
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
translateLinkLabel.setText(getWeblateLabelLink(tfComponent.getText().replaceAll("\n", Matcher.quoteReplacement("\n"))));
|
||||
translateLinkLabel.revalidate();
|
||||
translateLinkLabel.repaint();
|
||||
}
|
||||
});
|
||||
translateLinkLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
translateLinkLabel.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (e.getButton() == MouseEvent.BUTTON1) {
|
||||
try {
|
||||
Desktop.getDesktop().browse(new URI(WeblateIntegration.getWeblateLabelURI(tfComponent.getText().replaceAll("\n", Matcher.quoteReplacement("\n")))));
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
} catch (URISyntaxException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static JTextField addTranslatableTextField(JPanel pane, String label, String initialValue, boolean editable, final FieldUpdateListener listener) {
|
||||
final JTextField tfField = addTextField(pane, label, initialValue, editable, listener);
|
||||
addTranslationPane(pane, tfField, initialValue);
|
||||
return tfField;
|
||||
}
|
||||
|
||||
public static String getWeblateLabelLink(String text) {
|
||||
return "<html><a href=\""+WeblateIntegration.getWeblateLabelURI(text)+"\">Translate on weblate</a></html>";
|
||||
}
|
||||
|
||||
public static JTextField addTextField(JPanel pane, String label, String initialValue, boolean editable, final FieldUpdateListener listener) {
|
||||
JPanel tfPane = new JPanel();
|
||||
tfPane.setLayout(new JideBoxLayout(tfPane, JideBoxLayout.LINE_AXIS, 6));
|
||||
@@ -151,6 +256,13 @@ public abstract class Editor extends JPanel implements ProjectElementListener {
|
||||
return tfField;
|
||||
}
|
||||
|
||||
|
||||
public static JTextArea addTranslatableTextArea(JPanel pane, String label, String initialValue, boolean editable, final FieldUpdateListener listener) {
|
||||
final JTextArea tfArea = addTextArea(pane, label, initialValue, editable, listener);
|
||||
addTranslationPane(pane, tfArea, initialValue);
|
||||
return tfArea;
|
||||
}
|
||||
|
||||
public static JTextArea addTextArea(JPanel pane, String label, String initialValue, boolean editable, final FieldUpdateListener listener) {
|
||||
String text= initialValue == null ? "" : initialValue.replaceAll("\\n", "\n");
|
||||
|
||||
@@ -160,6 +272,9 @@ public abstract class Editor extends JPanel implements ProjectElementListener {
|
||||
tfPane.add(tfLabel, JideBoxLayout.FIX);
|
||||
final JTextArea tfArea = new JTextArea(text);
|
||||
tfArea.setEditable(editable);
|
||||
tfArea.setRows(2);
|
||||
tfArea.setLineWrap(true);
|
||||
tfArea.setWrapStyleWord(true);
|
||||
tfPane.add(new JScrollPane(tfArea), JideBoxLayout.VARY);
|
||||
JButton nullify = new JButton(new ImageIcon(DefaultIcons.getNullifyIcon()));
|
||||
tfPane.add(nullify, JideBoxLayout.FIX);
|
||||
@@ -534,8 +649,63 @@ public abstract class Editor extends JPanel implements ProjectElementListener {
|
||||
return gdeBox;
|
||||
}
|
||||
|
||||
public JComboBox<QuestStage> addQuestStageBox(JPanel pane, Project proj, String label, Integer initialValue, boolean writable, final FieldUpdateListener listener, Quest quest, final JComboBox questSelectionBox) {
|
||||
JPanel gdePane = new JPanel();
|
||||
gdePane.setLayout(new JideBoxLayout(gdePane, JideBoxLayout.LINE_AXIS, 6));
|
||||
JLabel gdeLabel = new JLabel(label);
|
||||
gdePane.add(gdeLabel, JideBoxLayout.FIX);
|
||||
|
||||
QuestStage initial = null;
|
||||
if (quest != null) {
|
||||
initial = quest.getStage(initialValue);
|
||||
}
|
||||
final QuestStageComboModel comboModel = new QuestStageComboModel(proj, initial, quest);
|
||||
final JComboBox<QuestStage> combo = new JComboBox<QuestStage>(comboModel);
|
||||
combo.setRenderer(new GDERenderer(false, writable));
|
||||
new ComboBoxSearchable(combo){
|
||||
@Override
|
||||
protected String convertElementToString(Object object) {
|
||||
if (object == null) return "none";
|
||||
else return ((GameDataElement)object).getDesc();
|
||||
}
|
||||
};
|
||||
questSelectionBox.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (comboModel.selected != null) {
|
||||
Editor.this.target.removeBacklink(comboModel.selected);
|
||||
}
|
||||
Quest newQuest = (Quest) questSelectionBox.getSelectedItem();
|
||||
comboModel.changeQuest(newQuest);
|
||||
combo.revalidate();
|
||||
}
|
||||
});
|
||||
combo.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
listener.valueChanged(combo, comboModel.selected == null ? null : comboModel.selected.progress);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
combo.setEnabled(writable);
|
||||
gdePane.add(combo, JideBoxLayout.VARY);
|
||||
|
||||
pane.add(gdePane, JideBoxLayout.FIX);
|
||||
|
||||
return combo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public JList addBacklinksList(JPanel pane, GameDataElement gde) {
|
||||
return addBacklinksList(pane, gde, "Elements linking to this one");
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public JList addBacklinksList(JPanel pane, GameDataElement gde, String title) {
|
||||
final JList list = new JList(new GDEBacklinksListModel(gde));
|
||||
list.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
@@ -556,7 +726,7 @@ public abstract class Editor extends JPanel implements ProjectElementListener {
|
||||
}
|
||||
});
|
||||
list.setCellRenderer(new GDERenderer(true, false));
|
||||
CollapsiblePanel colPane = new CollapsiblePanel("Elements linking to this one");
|
||||
CollapsiblePanel colPane = new CollapsiblePanel(title);
|
||||
colPane.setLayout(new JideBoxLayout(colPane, JideBoxLayout.PAGE_AXIS));
|
||||
colPane.add(new JScrollPane(list), JideBoxLayout.FIX);
|
||||
colPane.add(new JPanel(), JideBoxLayout.FIX);
|
||||
@@ -634,9 +804,25 @@ public abstract class Editor extends JPanel implements ProjectElementListener {
|
||||
label.setText("None"+(writable ? ". Click on the button to create one." : ""));
|
||||
} else {
|
||||
if (includeType && ((GameDataElement)value).getDataType() != null) {
|
||||
label.setText(((GameDataElement)value).getDataType().toString()+"/"+((GameDataElement)value).getDesc());
|
||||
if (value instanceof QuestStage) {
|
||||
String text = ((GameDataElement)value).getDesc();
|
||||
if (text.length() > 60) {
|
||||
text = text.substring(0, 57)+"...";
|
||||
}
|
||||
label.setText(((GameDataElement)value).getDataType().toString()+"/"+((Quest)((QuestStage)value).parent).id+":"+text);
|
||||
} else {
|
||||
label.setText(((GameDataElement)value).getDataType().toString()+"/"+((GameDataElement)value).getDesc());
|
||||
}
|
||||
} else {
|
||||
label.setText(((GameDataElement)value).getDesc());
|
||||
if (value instanceof QuestStage) {
|
||||
String text = ((GameDataElement)value).getDesc();
|
||||
if (text.length() > 60) {
|
||||
text = text.substring(0, 57)+"...";
|
||||
}
|
||||
label.setText(text);
|
||||
} else {
|
||||
label.setText(((GameDataElement)value).getDesc());
|
||||
}
|
||||
}
|
||||
if (((GameDataElement)value).getIcon() == null) {
|
||||
Notification.addError("Unable to find icon for "+((GameDataElement)value).getDesc());
|
||||
@@ -649,6 +835,65 @@ public abstract class Editor extends JPanel implements ProjectElementListener {
|
||||
|
||||
}
|
||||
|
||||
public static class QuestStageComboModel extends AbstractListModel<QuestStage> implements ComboBoxModel<QuestStage> {
|
||||
|
||||
private static final long serialVersionUID = -5854574666510314715L;
|
||||
|
||||
public Project project;
|
||||
public Quest currentQuest;
|
||||
public QuestStage selected;
|
||||
|
||||
public QuestStageComboModel(Project proj, QuestStage initial, Quest quest) {
|
||||
this.project = proj;
|
||||
this.currentQuest = quest;
|
||||
this.selected = initial;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
if (currentQuest == null) return 1;
|
||||
return currentQuest.stages.size()+1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QuestStage getElementAt(int index) {
|
||||
if (index == 0) {
|
||||
return null;
|
||||
}
|
||||
return currentQuest.stages.get(index - 1);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void setSelectedItem(Object anItem) {
|
||||
selected = (QuestStage) anItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getSelectedItem() {
|
||||
return selected;
|
||||
}
|
||||
|
||||
public void itemAdded(QuestStage item, int index) {
|
||||
fireIntervalAdded(this, index, index);
|
||||
}
|
||||
|
||||
public void itemRemoved(QuestStage item, int index) {
|
||||
fireIntervalRemoved(this, index, index);
|
||||
}
|
||||
|
||||
public void changeQuest(Quest newQuest) {
|
||||
int size = getSize();
|
||||
currentQuest = null;
|
||||
selected = null;
|
||||
fireIntervalRemoved(this, 1, size);
|
||||
currentQuest = newQuest;
|
||||
fireIntervalAdded(this, 1, getSize());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static class GDEBacklinksListModel implements ListModel<GameDataElement> {
|
||||
|
||||
GameDataElement source;
|
||||
@@ -782,7 +1027,5 @@ public abstract class Editor extends JPanel implements ProjectElementListener {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
100
src/com/gpl/rpg/atcontentstudio/ui/IdChangeImpactWizard.java
Normal file
@@ -0,0 +1,100 @@
|
||||
package com.gpl.rpg.atcontentstudio.ui;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
import javax.swing.DefaultListCellRenderer;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
|
||||
import com.gpl.rpg.atcontentstudio.ATContentStudio;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameDataElement;
|
||||
import com.jidesoft.swing.JideBoxLayout;
|
||||
|
||||
public class IdChangeImpactWizard extends JDialog {
|
||||
|
||||
private static final long serialVersionUID = 8532169707953315739L;
|
||||
|
||||
public static enum Result {
|
||||
ok, cancel
|
||||
}
|
||||
|
||||
Result result = null;
|
||||
|
||||
private IdChangeImpactWizard(GameDataElement changing, List<GameDataElement> toModify, List<GameDataElement> toAlter) {
|
||||
super(ATContentStudio.frame, true);
|
||||
JPanel pane = new JPanel();
|
||||
pane.setLayout(new JideBoxLayout(pane, JideBoxLayout.PAGE_AXIS));
|
||||
|
||||
pane.add(new JLabel("Changing the id for \""+changing.getDesc()+"\" has impacts on your project:"), JideBoxLayout.FIX);
|
||||
pane.add(new JLabel("The following elements from your project will be modified:"), JideBoxLayout.FIX);
|
||||
JList<GameDataElement> modifList = new JList<GameDataElement>(new Vector<GameDataElement>(toModify));
|
||||
modifList.setCellRenderer(new ChangeImpactListCellRenderer());
|
||||
pane.add(new JScrollPane(modifList), JideBoxLayout.FIX);
|
||||
pane.add(new JLabel("The following elements from the game source will be altered:"), JideBoxLayout.FIX);
|
||||
JList<GameDataElement> alterList = new JList<GameDataElement>(new Vector<GameDataElement>(toAlter));
|
||||
alterList.setCellRenderer(new ChangeImpactListCellRenderer());
|
||||
pane.add(new JScrollPane(alterList), JideBoxLayout.FIX);
|
||||
pane.add(new JLabel("Press Ok to apply the changes, or Cancel to cancel your edition of the object's ID"), JideBoxLayout.FIX);
|
||||
|
||||
JPanel buttonPane = new JPanel();
|
||||
buttonPane.setLayout(new JideBoxLayout(buttonPane, JideBoxLayout.LINE_AXIS));
|
||||
buttonPane.add(new JPanel(), JideBoxLayout.VARY);
|
||||
JButton cancelButton = new JButton("Cancel");
|
||||
cancelButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
result = Result.cancel;
|
||||
dispose();
|
||||
}
|
||||
});
|
||||
buttonPane.add(cancelButton, JideBoxLayout.FIX);
|
||||
JButton okButton = new JButton("Ok");
|
||||
okButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
result = Result.ok;
|
||||
dispose();
|
||||
}
|
||||
});
|
||||
buttonPane.add(okButton, JideBoxLayout.FIX);
|
||||
pane.add(buttonPane, JideBoxLayout.FIX);
|
||||
getContentPane().setLayout(new BorderLayout());
|
||||
getContentPane().add(pane, BorderLayout.CENTER);
|
||||
pack();
|
||||
}
|
||||
|
||||
|
||||
public static Result showIdChangeImapctWizard(GameDataElement changing, List<GameDataElement> toModify, List<GameDataElement> toAlter) {
|
||||
IdChangeImpactWizard wizard = new IdChangeImpactWizard(changing, toModify, toAlter);
|
||||
wizard.setVisible(true);
|
||||
return wizard.result;
|
||||
}
|
||||
|
||||
public class ChangeImpactListCellRenderer extends DefaultListCellRenderer {
|
||||
private static final long serialVersionUID = 5764079243906396333L;
|
||||
|
||||
@Override
|
||||
public Component getListCellRendererComponent(@SuppressWarnings("rawtypes") JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||
Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||
if (c instanceof JLabel) {
|
||||
JLabel label = (JLabel) c;
|
||||
GameDataElement target = ((GameDataElement)value);
|
||||
label.setIcon(new ImageIcon(target.getIcon()));
|
||||
label.setText(target.getDataType().toString()+"/"+target.getDesc());
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
|
||||
@@ -3,14 +3,11 @@ package com.gpl.rpg.atcontentstudio.ui;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Font;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
@@ -30,22 +27,13 @@ public class NotificationsPane extends JList {
|
||||
|
||||
private static final long serialVersionUID = -1100364214372392608L;
|
||||
|
||||
public static final String success_img_name = "/com/gpl/rpg/atcontentstudio/img/success.png";
|
||||
public static final String info_img_name = "/com/gpl/rpg/atcontentstudio/img/info.png";
|
||||
public static final String warn_img_name = "/com/gpl/rpg/atcontentstudio/img/warn.png";
|
||||
public static final String error_img_name = "/com/gpl/rpg/atcontentstudio/img/error.png";
|
||||
|
||||
public static final Map<Notification.Type, Icon> icons = new LinkedHashMap<Notification.Type, Icon>(Notification.Type.values().length);
|
||||
|
||||
static {
|
||||
try {
|
||||
icons.put(Notification.Type.SUCCESS, new ImageIcon(ImageIO.read(NotificationsPane.class.getResourceAsStream(success_img_name))));
|
||||
icons.put(Notification.Type.INFO, new ImageIcon(ImageIO.read(NotificationsPane.class.getResourceAsStream(info_img_name))));
|
||||
icons.put(Notification.Type.WARN, new ImageIcon(ImageIO.read(NotificationsPane.class.getResourceAsStream(warn_img_name))));
|
||||
icons.put(Notification.Type.ERROR, new ImageIcon(ImageIO.read(NotificationsPane.class.getResourceAsStream(error_img_name))));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
icons.put(Notification.Type.SUCCESS, new ImageIcon(DefaultIcons.getStatusGreenIcon()));
|
||||
icons.put(Notification.Type.INFO, new ImageIcon(DefaultIcons.getStatusBlueIcon()));
|
||||
icons.put(Notification.Type.WARN, new ImageIcon(DefaultIcons.getStatusOrangeIcon()));
|
||||
icons.put(Notification.Type.ERROR, new ImageIcon(DefaultIcons.getStatusRedIcon()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
|
||||
@@ -118,6 +118,7 @@ public class StudioFrame extends JFrame {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
Workspace.saveActive();
|
||||
actions.exitATCS.actionPerformed(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import java.awt.Toolkit;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import javax.swing.Action;
|
||||
@@ -95,14 +94,14 @@ public class WorkspaceActions {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (!(selectedNode instanceof GameDataElement)) return;
|
||||
final GameDataElement node = ((GameDataElement)selectedNode);
|
||||
if (node.state == GameDataElement.State.modified){
|
||||
if (node.needsSaving()){
|
||||
node.save();
|
||||
ATContentStudio.frame.nodeChanged(node);
|
||||
}
|
||||
};
|
||||
public void selectionChanged(ProjectTreeNode selectedNode, TreePath[] selectedPaths) {
|
||||
if (selectedNode instanceof GameDataElement) {
|
||||
setEnabled(((GameDataElement)selectedNode).state == GameDataElement.State.modified);
|
||||
setEnabled(((GameDataElement)selectedNode).needsSaving());
|
||||
} else {
|
||||
setEnabled(false);
|
||||
}
|
||||
@@ -330,8 +329,14 @@ public class WorkspaceActions {
|
||||
|
||||
public ATCSAction exitATCS = new ATCSAction("Exit", "Closes the program"){
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
//TODO ouch.
|
||||
System.exit(0);
|
||||
if (Workspace.activeWorkspace.needsSaving()) {
|
||||
int answer = JOptionPane.showConfirmDialog(ATContentStudio.frame, "There are unsaved changes in your workspace.\nExiting ATCS will discard these changes.\nDo you really want to exit?", "Unsaved changes. Confirm exit.", JOptionPane.YES_NO_OPTION);
|
||||
if (answer == JOptionPane.YES_OPTION) {
|
||||
System.exit(0);
|
||||
}
|
||||
} else {
|
||||
System.exit(0);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,10 @@ import java.awt.event.ActionListener;
|
||||
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JRadioButton;
|
||||
import javax.swing.JScrollPane;
|
||||
@@ -28,6 +31,10 @@ public class WorkspaceSettingsEditor extends JDialog {
|
||||
JRadioButton useSystemDefaultImageViewerButton, useSystemDefaultImageEditorButton, useCustomImageEditorButton;
|
||||
JTextField imageEditorCommandField;
|
||||
|
||||
JCheckBox translatorModeBox;
|
||||
JComboBox<String> translatorLanguagesBox;
|
||||
JCheckBox useInternetBox;
|
||||
|
||||
|
||||
|
||||
public WorkspaceSettingsEditor(WorkspaceSettings settings) {
|
||||
@@ -46,6 +53,7 @@ public class WorkspaceSettingsEditor extends JDialog {
|
||||
|
||||
|
||||
pane.add(getExternalToolsPane(), JideBoxLayout.FIX);
|
||||
pane.add(getTranslatorModePane(), JideBoxLayout.FIX);
|
||||
pane.add(new JPanel(), JideBoxLayout.VARY);
|
||||
|
||||
buttonPane.add(new JPanel(), JideBoxLayout.VARY);
|
||||
@@ -147,6 +155,37 @@ public class WorkspaceSettingsEditor extends JDialog {
|
||||
return pane;
|
||||
}
|
||||
|
||||
public JPanel getTranslatorModePane() {
|
||||
CollapsiblePanel pane = new CollapsiblePanel("Translator options");
|
||||
pane.setLayout(new JideBoxLayout(pane, JideBoxLayout.PAGE_AXIS));
|
||||
|
||||
translatorModeBox = new JCheckBox("Activate translator mode");
|
||||
pane.add(translatorModeBox, JideBoxLayout.FIX);
|
||||
|
||||
JPanel langPane = new JPanel();
|
||||
langPane.setLayout(new JideBoxLayout(langPane, JideBoxLayout.LINE_AXIS));
|
||||
langPane.add(new JLabel("Language code: "), JideBoxLayout.FIX);
|
||||
translatorLanguagesBox = new JComboBox<String>(WorkspaceSettings.LANGUAGE_LIST);
|
||||
langPane.add(translatorLanguagesBox);
|
||||
pane.add(langPane, JideBoxLayout.FIX);
|
||||
|
||||
pane.add(new JLabel("If your language isn't here, complain on the forums at https://andorstrail.com/"), JideBoxLayout.FIX);
|
||||
|
||||
useInternetBox = new JCheckBox("Allow connecting to internet to retrieve data from weblate.");
|
||||
pane.add(useInternetBox, JideBoxLayout.FIX);
|
||||
|
||||
translatorModeBox.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
translatorLanguagesBox.setEnabled(translatorModeBox.isSelected());
|
||||
useInternetBox.setEnabled(translatorModeBox.isSelected());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return pane;
|
||||
}
|
||||
|
||||
public void loadFromModel() {
|
||||
//Tiled
|
||||
useSystemDefaultMapEditorButton.setSelected(settings.useSystemDefaultMapEditor.getCurrentValue());
|
||||
@@ -157,6 +196,19 @@ public class WorkspaceSettingsEditor extends JDialog {
|
||||
useSystemDefaultImageEditorButton.setSelected(settings.useSystemDefaultImageEditor.getCurrentValue());
|
||||
useCustomImageEditorButton.setSelected(!(settings.useSystemDefaultImageViewer.getCurrentValue() || settings.useSystemDefaultImageEditor.getCurrentValue()));
|
||||
imageEditorCommandField.setText(settings.imageEditorCommand.getCurrentValue());
|
||||
//Translator
|
||||
if (settings.translatorLanguage.getCurrentValue() != null) {
|
||||
translatorModeBox.setSelected(true);
|
||||
translatorLanguagesBox.setSelectedItem(settings.translatorLanguage.getCurrentValue());
|
||||
translatorLanguagesBox.setEnabled(true);
|
||||
useInternetBox.setEnabled(true);
|
||||
} else {
|
||||
translatorModeBox.setSelected(false);
|
||||
translatorLanguagesBox.setSelectedItem(null);
|
||||
translatorLanguagesBox.setEnabled(false);
|
||||
useInternetBox.setEnabled(false);
|
||||
}
|
||||
useInternetBox.setSelected(settings.useInternet.getCurrentValue());
|
||||
}
|
||||
|
||||
public void pushToModel() {
|
||||
@@ -167,7 +219,13 @@ public class WorkspaceSettingsEditor extends JDialog {
|
||||
settings.useSystemDefaultImageViewer.setCurrentValue(useSystemDefaultImageViewerButton.isSelected());
|
||||
settings.useSystemDefaultImageEditor.setCurrentValue(useSystemDefaultImageEditorButton.isSelected());
|
||||
settings.imageEditorCommand.setCurrentValue(imageEditorCommandField.getText());
|
||||
|
||||
//Translator
|
||||
if (translatorModeBox.isSelected()) {
|
||||
settings.translatorLanguage.setCurrentValue((String)translatorLanguagesBox.getSelectedItem());
|
||||
} else {
|
||||
settings.translatorLanguage.resetDefault();
|
||||
}
|
||||
settings.useInternet.setCurrentValue(useInternetBox.isSelected());
|
||||
settings.save();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import java.awt.Dimension;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import java.awt.Dimension;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ public class ActorConditionEditor extends JSONElementEditor {
|
||||
acIcon = createButtonPane(pane, ac.getProject(), ac, ActorCondition.class, ac.getImage(), Spritesheet.Category.actorcondition, listener);
|
||||
|
||||
idField = addTextField(pane, "Internal ID: ", ac.id, ac.writable, listener);
|
||||
nameField = addTextField(pane, "Display name: ", ac.display_name, ac.writable, listener);
|
||||
nameField = addTranslatableTextField(pane, "Display name: ", ac.display_name, ac.writable, listener);
|
||||
categoryBox = addEnumValueBox(pane, "Category: ", ActorCondition.ACCategory.values(), ac.category, ac.writable, listener);
|
||||
positiveBox = addIntegerBasedCheckBox(pane, "Positive", ac.positive, ac.writable, listener);
|
||||
stackingBox = addIntegerBasedCheckBox(pane, "Stacking", ac.stacking, ac.writable, listener);
|
||||
@@ -147,10 +147,22 @@ public class ActorConditionEditor extends JSONElementEditor {
|
||||
public void valueChanged(JComponent source, Object value) {
|
||||
ActorCondition aCond = (ActorCondition)target;
|
||||
if (source == idField) {
|
||||
aCond.id = (String) value;
|
||||
ActorConditionEditor.this.name = aCond.getDesc();
|
||||
aCond.childrenChanged(new ArrayList<ProjectTreeNode>());
|
||||
ATContentStudio.frame.editorChanged(ActorConditionEditor.this);
|
||||
//Events caused by cancel an ID edition. Dismiss.
|
||||
if (skipNext) {
|
||||
skipNext = false;
|
||||
return;
|
||||
}
|
||||
if (target.id.equals((String) value)) return;
|
||||
|
||||
if (idChanging()) {
|
||||
aCond.id = (String) value;
|
||||
ActorConditionEditor.this.name = aCond.getDesc();
|
||||
aCond.childrenChanged(new ArrayList<ProjectTreeNode>());
|
||||
ATContentStudio.frame.editorChanged(ActorConditionEditor.this);
|
||||
} else {
|
||||
cancelIdEdit(idField);
|
||||
return;
|
||||
}
|
||||
} else if (source == nameField) {
|
||||
aCond.display_name = (String) value;
|
||||
ActorConditionEditor.this.name = aCond.getDesc();
|
||||
|
||||
@@ -43,6 +43,7 @@ import com.gpl.rpg.atcontentstudio.model.gamedata.Droplist;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.Item;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.NPC;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.Quest;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.QuestStage;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.Requirement;
|
||||
import com.gpl.rpg.atcontentstudio.model.maps.TMXMap;
|
||||
import com.gpl.rpg.atcontentstudio.ui.BooleanBasedCheckBox;
|
||||
@@ -94,7 +95,7 @@ public class DialogueEditor extends JSONElementEditor {
|
||||
private JTextField rewardObjId;
|
||||
private JComboBox rewardObjIdCombo;
|
||||
private MyComboBox rewardObj;
|
||||
private JSpinner rewardValue;
|
||||
private JComponent rewardValue;
|
||||
|
||||
private RepliesListModel repliesListModel;
|
||||
@SuppressWarnings("rawtypes")
|
||||
@@ -114,7 +115,7 @@ public class DialogueEditor extends JSONElementEditor {
|
||||
private JPanel requirementParamsPane;
|
||||
private MyComboBox requirementObj;
|
||||
private JTextField requirementObjId;
|
||||
private JSpinner requirementValue;
|
||||
private JComponent requirementValue;
|
||||
private BooleanBasedCheckBox requirementNegated;
|
||||
|
||||
private DialogueGraphView dialogueGraphView;
|
||||
@@ -165,7 +166,7 @@ public class DialogueEditor extends JSONElementEditor {
|
||||
createButtonPane(pane, dialogue.getProject(), dialogue, Dialogue.class, dialogue.getImage(), null, listener);
|
||||
|
||||
idField = addTextField(pane, "Internal ID: ", dialogue.id, dialogue.writable, listener);
|
||||
messageField = addTextArea(pane, "Message: ", dialogue.message, dialogue.writable, listener);
|
||||
messageField = addTranslatableTextArea(pane, "Message: ", dialogue.message, dialogue.writable, listener);
|
||||
switchToNpcBox = addNPCBox(pane, dialogue.getProject(), "Switch active NPC to: ", dialogue.switch_to_npc, dialogue.writable, listener);
|
||||
|
||||
CollapsiblePanel rewards = new CollapsiblePanel("Reaching this phrase gives the following rewards: ");
|
||||
@@ -421,7 +422,7 @@ public class DialogueEditor extends JSONElementEditor {
|
||||
rewardObjId = null;
|
||||
rewardObjIdCombo = null;
|
||||
rewardObj = addQuestBox(pane, ((Dialogue)target).getProject(), "Quest: ", (Quest) reward.reward_obj, writable, listener);
|
||||
rewardValue = addIntegerField(pane, "Step ID: ", reward.reward_value, false, writable, listener);
|
||||
rewardValue = addQuestStageBox(pane, ((Dialogue)target).getProject(), "Quest stage: ", reward.reward_value, writable, listener, (Quest) reward.reward_obj, rewardObj);
|
||||
break;
|
||||
case skillIncrease:
|
||||
rewardMap = null;
|
||||
@@ -613,10 +614,10 @@ public class DialogueEditor extends JSONElementEditor {
|
||||
replyText = null;
|
||||
replyNextPhrase = addDialogueBox(pane, ((Dialogue)target).getProject(), "Next phrase: ", reply.next_phrase, writable, listener);
|
||||
} else if (Dialogue.Reply.KEY_PHRASE_ID.contains(reply.next_phrase_id)) {
|
||||
replyText = addTextField(pane, "Reply text: ", reply.text, writable, listener);
|
||||
replyText = addTranslatableTextField(pane, "Reply text: ", reply.text, writable, listener);
|
||||
replyNextPhrase = null;
|
||||
} else {
|
||||
replyText = addTextField(pane, "Reply text: ", reply.text, writable, listener);
|
||||
replyText = addTranslatableTextField(pane, "Reply text: ", reply.text, writable, listener);
|
||||
replyNextPhrase = addDialogueBox(pane, ((Dialogue)target).getProject(), "Next phrase: ", reply.next_phrase, writable, listener);
|
||||
}
|
||||
|
||||
@@ -679,7 +680,7 @@ public class DialogueEditor extends JSONElementEditor {
|
||||
case questProgress:
|
||||
requirementObj = addQuestBox(pane, project, "Quest: ", (Quest) requirement.required_obj, writable, listener);
|
||||
requirementObjId = null;
|
||||
requirementValue = addIntegerField(pane, "Quest stage: ", requirement.required_value, false, writable, listener);
|
||||
requirementValue = addQuestStageBox(pane, project, "Quest stage: ", requirement.required_value, writable, listener, (Quest) requirement.required_obj, requirementObj);
|
||||
break;
|
||||
case skillLevel:
|
||||
requirementObj = null;
|
||||
@@ -1075,10 +1076,22 @@ public class DialogueEditor extends JSONElementEditor {
|
||||
public void valueChanged(JComponent source, Object value) {
|
||||
Dialogue dialogue = (Dialogue) target;
|
||||
if (source == idField) {
|
||||
dialogue.id = (String) value;
|
||||
DialogueEditor.this.name = dialogue.getDesc();
|
||||
dialogue.childrenChanged(new ArrayList<ProjectTreeNode>());
|
||||
ATContentStudio.frame.editorChanged(DialogueEditor.this);
|
||||
//Events caused by cancel an ID edition. Dismiss.
|
||||
if (skipNext) {
|
||||
skipNext = false;
|
||||
return;
|
||||
}
|
||||
if (target.id.equals((String) value)) return;
|
||||
|
||||
if (idChanging()) {
|
||||
dialogue.id = (String) value;
|
||||
DialogueEditor.this.name = dialogue.getDesc();
|
||||
dialogue.childrenChanged(new ArrayList<ProjectTreeNode>());
|
||||
ATContentStudio.frame.editorChanged(DialogueEditor.this);
|
||||
} else {
|
||||
cancelIdEdit(idField);
|
||||
return;
|
||||
}
|
||||
} else if (source == messageField) {
|
||||
dialogue.message = (String) value;
|
||||
} else if (source == switchToNpcBox) {
|
||||
@@ -1137,7 +1150,21 @@ public class DialogueEditor extends JSONElementEditor {
|
||||
}
|
||||
rewardsListModel.itemChanged(selectedReward);
|
||||
} else if (source == rewardValue) {
|
||||
//Backlink removal to quest stages when selecting another quest are handled in the addQuestStageBox() method. Too complex too handle here
|
||||
Quest quest = null;
|
||||
QuestStage stage = null;
|
||||
if (rewardValue instanceof JComboBox<?>) {
|
||||
quest = ((Quest)selectedReward.reward_obj);
|
||||
if (quest != null && selectedReward.reward_value != null) {
|
||||
stage = quest.getStage(selectedReward.reward_value);
|
||||
if (stage != null) stage.removeBacklink(dialogue);
|
||||
}
|
||||
}
|
||||
selectedReward.reward_value = (Integer) value;
|
||||
if (quest != null) {
|
||||
stage = quest.getStage(selectedReward.reward_value);
|
||||
if (stage != null) stage.addBacklink(dialogue);
|
||||
}
|
||||
rewardsListModel.itemChanged(selectedReward);
|
||||
} else if (source == replyTypeCombo) {
|
||||
updateRepliesParamsEditorPane(repliesParamsPane, selectedReply, this);
|
||||
@@ -1178,7 +1205,21 @@ public class DialogueEditor extends JSONElementEditor {
|
||||
selectedRequirement.required_obj = null;
|
||||
requirementsListModel.itemChanged(selectedRequirement);
|
||||
} else if (source == requirementValue) {
|
||||
//Backlink removal to quest stages when selecting another quest are handled in the addQuestStageBox() method. Too complex too handle here
|
||||
Quest quest = null;
|
||||
QuestStage stage = null;
|
||||
if (requirementValue instanceof JComboBox<?>) {
|
||||
quest = ((Quest)selectedRequirement.required_obj);
|
||||
if (quest != null && selectedRequirement.required_value != null) {
|
||||
stage = quest.getStage(selectedRequirement.required_value);
|
||||
if (stage != null) stage.removeBacklink(dialogue);
|
||||
}
|
||||
}
|
||||
selectedRequirement.required_value = (Integer) value;
|
||||
if (quest != null) {
|
||||
stage = quest.getStage(selectedRequirement.required_value);
|
||||
if (stage != null) stage.addBacklink(dialogue);
|
||||
}
|
||||
requirementsListModel.itemChanged(selectedRequirement);
|
||||
} else if (source == requirementNegated) {
|
||||
selectedRequirement.negated = (Boolean) value;
|
||||
|
||||
@@ -248,10 +248,22 @@ public class DroplistEditor extends JSONElementEditor {
|
||||
public void valueChanged(JComponent source, Object value) {
|
||||
Droplist droplist = ((Droplist)target);
|
||||
if (source == idField) {
|
||||
droplist.id = (String) value;
|
||||
DroplistEditor.this.name = droplist.getDesc();
|
||||
droplist.childrenChanged(new ArrayList<ProjectTreeNode>());
|
||||
ATContentStudio.frame.editorChanged(DroplistEditor.this);
|
||||
//Events caused by cancel an ID edition. Dismiss.
|
||||
if (skipNext) {
|
||||
skipNext = false;
|
||||
return;
|
||||
}
|
||||
if (target.id.equals((String) value)) return;
|
||||
|
||||
if (idChanging()) {
|
||||
droplist.id = (String) value;
|
||||
DroplistEditor.this.name = droplist.getDesc();
|
||||
droplist.childrenChanged(new ArrayList<ProjectTreeNode>());
|
||||
ATContentStudio.frame.editorChanged(DroplistEditor.this);
|
||||
} else {
|
||||
cancelIdEdit(idField);
|
||||
return;
|
||||
}
|
||||
} else if (source == itemCombo) {
|
||||
if (selectedItem.item != null) {
|
||||
selectedItem.item.removeBacklink(droplist);
|
||||
|
||||
@@ -52,7 +52,7 @@ public class ItemCategoryEditor extends JSONElementEditor {
|
||||
|
||||
|
||||
idField = addTextField(pane, "Internal ID: ", ic.id, ic.writable, listener);
|
||||
nameField = addTextField(pane, "Display name: ", ic.name, ic.writable, listener);
|
||||
nameField = addTranslatableTextField(pane, "Display name: ", ic.name, ic.writable, listener);
|
||||
typeBox = addEnumValueBox(pane, "Action type: ", ItemCategory.ActionType.values(), ic.action_type, ic.writable, listener);
|
||||
slotBox = addEnumValueBox(pane, "Inventory slot: ", ItemCategory.InventorySlot.values(), ic.slot, ic.writable, listener);
|
||||
sizeBox = addEnumValueBox(pane, "Item size: ", ItemCategory.Size.values(), ic.size, ic.writable, listener);
|
||||
@@ -87,10 +87,22 @@ public class ItemCategoryEditor extends JSONElementEditor {
|
||||
public void valueChanged(JComponent source, Object value) {
|
||||
ItemCategory ic = (ItemCategory)target;
|
||||
if (source == idField) {
|
||||
ic.id = (String) value;
|
||||
ItemCategoryEditor.this.name = ic.getDesc();
|
||||
ic.childrenChanged(new ArrayList<ProjectTreeNode>());
|
||||
ATContentStudio.frame.editorChanged(ItemCategoryEditor.this);
|
||||
//Events caused by cancel an ID edition. Dismiss.
|
||||
if (skipNext) {
|
||||
skipNext = false;
|
||||
return;
|
||||
}
|
||||
if (target.id.equals((String) value)) return;
|
||||
|
||||
if (idChanging()) {
|
||||
ic.id = (String) value;
|
||||
ItemCategoryEditor.this.name = ic.getDesc();
|
||||
ic.childrenChanged(new ArrayList<ProjectTreeNode>());
|
||||
ATContentStudio.frame.editorChanged(ItemCategoryEditor.this);
|
||||
} else {
|
||||
cancelIdEdit(idField);
|
||||
return;
|
||||
}
|
||||
} else if (source == nameField) {
|
||||
ic.name = (String) value;
|
||||
ItemCategoryEditor.this.name = ic.getDesc();
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.DefaultListCellRenderer;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
@@ -15,6 +16,7 @@ import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JRadioButton;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.JTextField;
|
||||
@@ -98,6 +100,8 @@ public class ItemEditor extends JSONElementEditor {
|
||||
@SuppressWarnings("rawtypes")
|
||||
private JList hitSourceConditionsList;
|
||||
private MyComboBox hitSourceConditionBox;
|
||||
private JRadioButton hitSourceConditionClear;
|
||||
private JRadioButton hitSourceConditionApply;
|
||||
private JSpinner hitSourceConditionMagnitude;
|
||||
private JSpinner hitSourceConditionDuration;
|
||||
private JSpinner hitSourceConditionChance;
|
||||
@@ -105,6 +109,8 @@ public class ItemEditor extends JSONElementEditor {
|
||||
@SuppressWarnings("rawtypes")
|
||||
private JList hitTargetConditionsList;
|
||||
private MyComboBox hitTargetConditionBox;
|
||||
private JRadioButton hitTargetConditionClear;
|
||||
private JRadioButton hitTargetConditionApply;
|
||||
private JSpinner hitTargetConditionMagnitude;
|
||||
private JSpinner hitTargetConditionDuration;
|
||||
private JSpinner hitTargetConditionChance;
|
||||
@@ -119,6 +125,8 @@ public class ItemEditor extends JSONElementEditor {
|
||||
@SuppressWarnings("rawtypes")
|
||||
private JList killSourceConditionsList;
|
||||
private MyComboBox killSourceConditionBox;
|
||||
private JRadioButton killSourceConditionClear;
|
||||
private JRadioButton killSourceConditionApply;
|
||||
private JSpinner killSourceConditionMagnitude;
|
||||
private JSpinner killSourceConditionDuration;
|
||||
private JSpinner killSourceConditionChance;
|
||||
@@ -141,8 +149,8 @@ public class ItemEditor extends JSONElementEditor {
|
||||
itemIcon = createButtonPane(pane, item.getProject(), item, Item.class, item.getImage(), Spritesheet.Category.item, listener);
|
||||
|
||||
idField = addTextField(pane, "Internal ID: ", item.id, item.writable, listener);
|
||||
nameField = addTextField(pane, "Display name: ", item.name, item.writable, listener);
|
||||
descriptionField = addTextField(pane, "Description: ", item.description, item.writable, listener);
|
||||
nameField = addTranslatableTextField(pane, "Display name: ", item.name, item.writable, listener);
|
||||
descriptionField = addTranslatableTextField(pane, "Description: ", item.description, item.writable, listener);
|
||||
typeBox = addEnumValueBox(pane, "Type: ", Item.DisplayType.values(), item.display_type, item.writable, listener);
|
||||
manualPriceBox = addIntegerBasedCheckBox(pane, "Has manual price", item.has_manual_price, item.writable, listener);
|
||||
baseManualPrice = item.base_market_cost;
|
||||
@@ -461,55 +469,169 @@ public class ItemEditor extends JSONElementEditor {
|
||||
|
||||
}
|
||||
|
||||
public void updateHitSourceTimedConditionEditorPane(JPanel pane, Item.TimedConditionEffect condition, FieldUpdateListener listener) {
|
||||
public void updateHitSourceTimedConditionEditorPane(JPanel pane, Item.TimedConditionEffect condition, final FieldUpdateListener listener) {
|
||||
pane.removeAll();
|
||||
if (hitSourceConditionBox != null) {
|
||||
removeElementListener(hitSourceConditionBox);
|
||||
}
|
||||
if (condition == null) {
|
||||
pane.revalidate();
|
||||
pane.repaint();
|
||||
return;
|
||||
}
|
||||
|
||||
boolean writable = ((Item)target).writable;
|
||||
Project proj = ((Item)target).getProject();
|
||||
|
||||
hitSourceConditionBox = addActorConditionBox(pane, proj, "Actor Condition: ", condition.condition, writable, listener);
|
||||
hitSourceConditionMagnitude = addIntegerField(pane, "Magnitude: ", condition.magnitude, false, writable, listener);
|
||||
hitSourceConditionDuration = addIntegerField(pane, "Duration: ", condition.duration, false, writable, listener);
|
||||
hitSourceConditionChance = addDoubleField(pane, "Chance: ", condition.chance, writable, listener);
|
||||
hitSourceConditionApply = new JRadioButton("Apply new condition");
|
||||
pane.add(hitSourceConditionApply, JideBoxLayout.FIX);
|
||||
hitSourceConditionMagnitude = addIntegerField(pane, "Magnitude: ", condition.magnitude == null ? null : condition.magnitude >= 0 ? condition.magnitude : 0, false, writable, listener);
|
||||
hitSourceConditionDuration = addIntegerField(pane, "Duration: ", condition.duration, false, writable, listener);
|
||||
hitSourceConditionClear = new JRadioButton("Clear active condition");
|
||||
pane.add(hitSourceConditionClear, JideBoxLayout.FIX);
|
||||
|
||||
ButtonGroup radioGroup = new ButtonGroup();
|
||||
radioGroup.add(hitSourceConditionApply);
|
||||
radioGroup.add(hitSourceConditionClear);
|
||||
|
||||
if (condition != null && condition.magnitude != null && condition.magnitude == ActorCondition.CLEAR_AC_MAGNITUDE) {
|
||||
hitSourceConditionClear.setSelected(true);
|
||||
hitSourceConditionApply.setSelected(false);
|
||||
hitSourceConditionMagnitude.setEnabled(false);
|
||||
hitSourceConditionDuration.setEnabled(false);
|
||||
} else {
|
||||
hitSourceConditionClear.setSelected(false);
|
||||
hitSourceConditionApply.setSelected(true);
|
||||
hitSourceConditionMagnitude.setEnabled(true);
|
||||
hitSourceConditionDuration.setEnabled(true);
|
||||
}
|
||||
|
||||
hitSourceConditionClear.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
listener.valueChanged(hitSourceConditionClear, new Boolean(hitSourceConditionClear.isSelected()));
|
||||
}
|
||||
});
|
||||
hitSourceConditionApply.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
listener.valueChanged(hitSourceConditionApply, new Boolean(hitSourceConditionApply.isSelected()));
|
||||
}
|
||||
});
|
||||
|
||||
pane.revalidate();
|
||||
pane.repaint();
|
||||
}
|
||||
|
||||
public void updateHitTargetTimedConditionEditorPane(JPanel pane, Item.TimedConditionEffect condition, FieldUpdateListener listener) {
|
||||
public void updateHitTargetTimedConditionEditorPane(JPanel pane, Item.TimedConditionEffect condition, final FieldUpdateListener listener) {
|
||||
pane.removeAll();
|
||||
if (hitTargetConditionBox != null) {
|
||||
removeElementListener(hitTargetConditionBox);
|
||||
}
|
||||
if (condition == null) {
|
||||
pane.revalidate();
|
||||
pane.repaint();
|
||||
return;
|
||||
}
|
||||
|
||||
boolean writable = ((Item)target).writable;
|
||||
Project proj = ((Item)target).getProject();
|
||||
|
||||
hitTargetConditionBox = addActorConditionBox(pane, proj, "Actor Condition: ", condition.condition, writable, listener);
|
||||
hitTargetConditionMagnitude = addIntegerField(pane, "Magnitude: ", condition.magnitude, false, writable, listener);
|
||||
hitTargetConditionDuration = addIntegerField(pane, "Duration: ", condition.duration, false, writable, listener);
|
||||
hitTargetConditionChance = addDoubleField(pane, "Chance: ", condition.chance, writable, listener);
|
||||
|
||||
hitTargetConditionApply = new JRadioButton("Apply new condition");
|
||||
pane.add(hitTargetConditionApply, JideBoxLayout.FIX);
|
||||
hitTargetConditionMagnitude = addIntegerField(pane, "Magnitude: ", condition.magnitude == null ? null : condition.magnitude >= 0 ? condition.magnitude : 0, false, writable, listener);
|
||||
hitTargetConditionDuration = addIntegerField(pane, "Duration: ", condition.duration, false, writable, listener);
|
||||
hitTargetConditionClear = new JRadioButton("Clear active condition");
|
||||
pane.add(hitTargetConditionClear, JideBoxLayout.FIX);
|
||||
|
||||
ButtonGroup radioGroup = new ButtonGroup();
|
||||
radioGroup.add(hitTargetConditionApply);
|
||||
radioGroup.add(hitTargetConditionClear);
|
||||
|
||||
if (condition != null && condition.magnitude != null && condition.magnitude == ActorCondition.CLEAR_AC_MAGNITUDE) {
|
||||
hitTargetConditionClear.setSelected(true);
|
||||
hitTargetConditionApply.setSelected(false);
|
||||
hitTargetConditionMagnitude.setEnabled(false);
|
||||
hitTargetConditionDuration.setEnabled(false);
|
||||
} else {
|
||||
hitTargetConditionClear.setSelected(false);
|
||||
hitTargetConditionApply.setSelected(true);
|
||||
hitTargetConditionMagnitude.setEnabled(true);
|
||||
hitTargetConditionDuration.setEnabled(true);
|
||||
}
|
||||
|
||||
hitTargetConditionClear.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
listener.valueChanged(hitTargetConditionClear, new Boolean(hitTargetConditionClear.isSelected()));
|
||||
}
|
||||
});
|
||||
hitTargetConditionApply.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
listener.valueChanged(hitTargetConditionApply, new Boolean(hitTargetConditionApply.isSelected()));
|
||||
}
|
||||
});
|
||||
|
||||
pane.revalidate();
|
||||
pane.repaint();
|
||||
}
|
||||
|
||||
public void updateKillSourceTimedConditionEditorPane(JPanel pane, Item.TimedConditionEffect condition, FieldUpdateListener listener) {
|
||||
public void updateKillSourceTimedConditionEditorPane(JPanel pane, Item.TimedConditionEffect condition, final FieldUpdateListener listener) {
|
||||
pane.removeAll();
|
||||
if (killSourceConditionBox != null) {
|
||||
removeElementListener(killSourceConditionBox);
|
||||
}
|
||||
if (condition == null) {
|
||||
pane.revalidate();
|
||||
pane.repaint();
|
||||
return;
|
||||
}
|
||||
|
||||
boolean writable = ((Item)target).writable;
|
||||
Project proj = ((Item)target).getProject();
|
||||
|
||||
killSourceConditionBox = addActorConditionBox(pane, proj, "Actor Condition: ", condition.condition, writable, listener);
|
||||
killSourceConditionMagnitude = addIntegerField(pane, "Magnitude: ", condition.magnitude, false, writable, listener);
|
||||
killSourceConditionDuration = addIntegerField(pane, "Duration: ", condition.duration, false, writable, listener);
|
||||
killSourceConditionChance = addDoubleField(pane, "Chance: ", condition.chance, writable, listener);
|
||||
|
||||
killSourceConditionApply = new JRadioButton("Apply new condition");
|
||||
pane.add(killSourceConditionApply, JideBoxLayout.FIX);
|
||||
killSourceConditionMagnitude = addIntegerField(pane, "Magnitude: ", condition.magnitude == null ? null : condition.magnitude >= 0 ? condition.magnitude : 0, false, writable, listener);
|
||||
killSourceConditionDuration = addIntegerField(pane, "Duration: ", condition.duration, false, writable, listener);
|
||||
killSourceConditionClear = new JRadioButton("Clear active condition");
|
||||
pane.add(killSourceConditionClear, JideBoxLayout.FIX);
|
||||
|
||||
ButtonGroup radioGroup = new ButtonGroup();
|
||||
radioGroup.add(killSourceConditionApply);
|
||||
radioGroup.add(killSourceConditionClear);
|
||||
|
||||
if (condition != null && condition.magnitude != null && condition.magnitude == ActorCondition.CLEAR_AC_MAGNITUDE) {
|
||||
killSourceConditionClear.setSelected(true);
|
||||
killSourceConditionApply.setSelected(false);
|
||||
killSourceConditionMagnitude.setEnabled(false);
|
||||
killSourceConditionDuration.setEnabled(false);
|
||||
} else {
|
||||
killSourceConditionClear.setSelected(false);
|
||||
killSourceConditionApply.setSelected(true);
|
||||
killSourceConditionMagnitude.setEnabled(true);
|
||||
killSourceConditionDuration.setEnabled(true);
|
||||
}
|
||||
|
||||
killSourceConditionClear.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
listener.valueChanged(killSourceConditionClear, new Boolean(killSourceConditionClear.isSelected()));
|
||||
}
|
||||
});
|
||||
killSourceConditionApply.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
listener.valueChanged(killSourceConditionApply, new Boolean(killSourceConditionApply.isSelected()));
|
||||
}
|
||||
});
|
||||
pane.revalidate();
|
||||
pane.repaint();
|
||||
}
|
||||
@@ -519,6 +641,11 @@ public class ItemEditor extends JSONElementEditor {
|
||||
if (equipConditionBox != null) {
|
||||
removeElementListener(equipConditionBox);
|
||||
}
|
||||
if (condition == null) {
|
||||
pane.revalidate();
|
||||
pane.repaint();
|
||||
return;
|
||||
}
|
||||
|
||||
boolean writable = ((Item)target).writable;
|
||||
Project proj = ((Item)target).getProject();
|
||||
@@ -666,7 +793,11 @@ public class ItemEditor extends JSONElementEditor {
|
||||
|
||||
if (effect.condition != null) {
|
||||
label.setIcon(new ImageIcon(effect.condition.getIcon()));
|
||||
label.setText(effect.chance+"% chances to give "+effect.duration+" rounds of "+effect.condition.getDesc()+" x"+effect.magnitude);
|
||||
if (effect.magnitude == ActorCondition.CLEAR_AC_MAGNITUDE) {
|
||||
label.setText(effect.chance+"% chances to clear "+effect.condition.getDesc());
|
||||
} else {
|
||||
label.setText(effect.chance+"% chances to give "+effect.duration+" rounds of "+effect.condition.getDesc()+" x"+effect.magnitude);
|
||||
}
|
||||
} else {
|
||||
label.setText("New, undefined actor condition effect.");
|
||||
}
|
||||
@@ -805,10 +936,22 @@ public class ItemEditor extends JSONElementEditor {
|
||||
boolean updatePrice, updateEquip, updateHit, updateKill;
|
||||
updatePrice = updateEquip = updateHit = updateKill = false;
|
||||
if (source == idField) {
|
||||
item.id = (String) value;
|
||||
ItemEditor.this.name = item.getDesc();
|
||||
item.childrenChanged(new ArrayList<ProjectTreeNode>());
|
||||
ATContentStudio.frame.editorChanged(ItemEditor.this);
|
||||
//Events caused by cancel an ID edition. Dismiss.
|
||||
if (skipNext) {
|
||||
skipNext = false;
|
||||
return;
|
||||
}
|
||||
if (target.id.equals((String) value)) return;
|
||||
|
||||
if (idChanging()) {
|
||||
item.id = (String) value;
|
||||
ItemEditor.this.name = item.getDesc();
|
||||
item.childrenChanged(new ArrayList<ProjectTreeNode>());
|
||||
ATContentStudio.frame.editorChanged(ItemEditor.this);
|
||||
} else {
|
||||
cancelIdEdit(idField);
|
||||
return;
|
||||
}
|
||||
} else if (source == nameField) {
|
||||
item.name = (String) value;
|
||||
ItemEditor.this.name = item.getDesc();
|
||||
@@ -984,6 +1127,20 @@ public class ItemEditor extends JSONElementEditor {
|
||||
}
|
||||
hitSourceConditionsModel.itemChanged(selectedHitEffectSourceCondition);
|
||||
updateHit = true;
|
||||
} else if (source == hitSourceConditionClear) {
|
||||
selectedHitEffectSourceCondition.magnitude = ActorCondition.CLEAR_AC_MAGNITUDE;
|
||||
selectedHitEffectSourceCondition.duration = null;
|
||||
hitSourceConditionMagnitude.setEnabled(false);
|
||||
hitSourceConditionDuration.setEnabled(false);
|
||||
hitSourceConditionsModel.itemChanged(selectedHitEffectSourceCondition);
|
||||
updateHit = true;
|
||||
} else if (source == hitSourceConditionApply) {
|
||||
selectedHitEffectSourceCondition.magnitude = 0;
|
||||
selectedHitEffectSourceCondition.duration = 0;
|
||||
hitSourceConditionMagnitude.setEnabled(true);
|
||||
hitSourceConditionDuration.setEnabled(true);
|
||||
hitSourceConditionsModel.itemChanged(selectedHitEffectSourceCondition);
|
||||
updateHit = true;
|
||||
} else if (source == hitSourceConditionMagnitude) {
|
||||
selectedHitEffectSourceCondition.magnitude = (Integer) value;
|
||||
hitSourceConditionsModel.itemChanged(selectedHitEffectSourceCondition);
|
||||
@@ -1011,6 +1168,20 @@ public class ItemEditor extends JSONElementEditor {
|
||||
}
|
||||
hitTargetConditionsModel.itemChanged(selectedHitEffectTargetCondition);
|
||||
updateHit = true;
|
||||
} else if (source == hitTargetConditionClear) {
|
||||
selectedHitEffectTargetCondition.magnitude = ActorCondition.CLEAR_AC_MAGNITUDE;
|
||||
selectedHitEffectTargetCondition.duration = null;
|
||||
hitTargetConditionMagnitude.setEnabled(false);
|
||||
hitTargetConditionDuration.setEnabled(false);
|
||||
hitTargetConditionsModel.itemChanged(selectedHitEffectTargetCondition);
|
||||
updateHit = true;
|
||||
} else if (source == hitTargetConditionApply) {
|
||||
selectedHitEffectTargetCondition.magnitude = 0;
|
||||
selectedHitEffectTargetCondition.duration = 0;
|
||||
hitTargetConditionMagnitude.setEnabled(true);
|
||||
hitTargetConditionDuration.setEnabled(true);
|
||||
hitTargetConditionsModel.itemChanged(selectedHitEffectTargetCondition);
|
||||
updateHit = true;
|
||||
} else if (source == hitTargetConditionMagnitude) {
|
||||
selectedHitEffectTargetCondition.magnitude = (Integer) value;
|
||||
hitTargetConditionsModel.itemChanged(selectedHitEffectTargetCondition);
|
||||
@@ -1054,6 +1225,20 @@ public class ItemEditor extends JSONElementEditor {
|
||||
}
|
||||
killSourceConditionsModel.itemChanged(selectedKillEffectCondition);
|
||||
updateKill = true;
|
||||
} else if (source == killSourceConditionClear) {
|
||||
selectedKillEffectCondition.magnitude = ActorCondition.CLEAR_AC_MAGNITUDE;
|
||||
selectedKillEffectCondition.duration = null;
|
||||
killSourceConditionMagnitude.setEnabled(false);
|
||||
killSourceConditionDuration.setEnabled(false);
|
||||
killSourceConditionsModel.itemChanged(selectedKillEffectCondition);
|
||||
updateKill = true;
|
||||
} else if (source == killSourceConditionApply) {
|
||||
selectedKillEffectCondition.magnitude = 0;
|
||||
selectedKillEffectCondition.duration = 0;
|
||||
killSourceConditionMagnitude.setEnabled(true);
|
||||
killSourceConditionDuration.setEnabled(true);
|
||||
killSourceConditionsModel.itemChanged(selectedKillEffectCondition);
|
||||
updateKill = true;
|
||||
} else if (source == killSourceConditionMagnitude) {
|
||||
selectedKillEffectCondition.magnitude = (Integer) value;
|
||||
killSourceConditionsModel.itemChanged(selectedKillEffectCondition);
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -14,6 +15,9 @@ import javax.swing.JButton;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.ScrollPaneConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
|
||||
@@ -26,13 +30,21 @@ import com.gpl.rpg.atcontentstudio.model.ProjectTreeNode;
|
||||
import com.gpl.rpg.atcontentstudio.model.SaveEvent;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.GameDataCategory;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.JSONElement;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.Quest;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.QuestStage;
|
||||
import com.gpl.rpg.atcontentstudio.model.maps.TMXMap;
|
||||
import com.gpl.rpg.atcontentstudio.model.maps.WorldmapSegment;
|
||||
import com.gpl.rpg.atcontentstudio.model.sprites.Spritesheet;
|
||||
import com.gpl.rpg.atcontentstudio.ui.DefaultIcons;
|
||||
import com.gpl.rpg.atcontentstudio.ui.Editor;
|
||||
import com.gpl.rpg.atcontentstudio.ui.FieldUpdateListener;
|
||||
import com.gpl.rpg.atcontentstudio.ui.IdChangeImpactWizard;
|
||||
import com.gpl.rpg.atcontentstudio.ui.SaveItemsWizard;
|
||||
import com.gpl.rpg.atcontentstudio.ui.ScrollablePanel;
|
||||
import com.gpl.rpg.atcontentstudio.ui.ScrollablePanel.ScrollableSizeHint;
|
||||
import com.gpl.rpg.atcontentstudio.ui.sprites.SpriteChooser;
|
||||
import com.jidesoft.swing.JideBoxLayout;
|
||||
import com.jidesoft.swing.JideScrollPane;
|
||||
import com.jidesoft.swing.JideTabbedPane;
|
||||
|
||||
public abstract class JSONElementEditor extends Editor {
|
||||
@@ -59,7 +71,11 @@ public abstract class JSONElementEditor extends Editor {
|
||||
}
|
||||
|
||||
public void addEditorTab(String id, JPanel editor) {
|
||||
JScrollPane scroller = new JScrollPane(editor);
|
||||
ScrollablePanel view = new ScrollablePanel(new BorderLayout());
|
||||
view.setScrollableWidth(ScrollableSizeHint.FIT);
|
||||
view.setScrollableHeight(ScrollableSizeHint.STRETCH);
|
||||
view.add(editor, BorderLayout.CENTER);
|
||||
JScrollPane scroller = new JScrollPane(view, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
scroller.getVerticalScrollBar().setUnitIncrement(16);
|
||||
editorTabsHolder.addTab(id, scroller);
|
||||
editorTabs.put(id, editor);
|
||||
@@ -172,8 +188,17 @@ public abstract class JSONElementEditor extends Editor {
|
||||
if (node.getParent() instanceof GameDataCategory<?>) {
|
||||
((GameDataCategory<?>)node.getParent()).remove(node);
|
||||
node.save();
|
||||
GameDataElement newOne = proj.getGameDataElement(node.getClass(), node.id);
|
||||
if (node instanceof Quest) {
|
||||
for (QuestStage oldStage : ((Quest) node).stages) {
|
||||
QuestStage newStage = newOne != null ? ((Quest) newOne).getStage(oldStage.progress) : null;
|
||||
for (GameDataElement backlink : oldStage.getBacklinks()) {
|
||||
backlink.elementChanged(oldStage, newStage);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (GameDataElement backlink : node.getBacklinks()) {
|
||||
backlink.elementChanged(node, proj.getGameDataElement(node.getClass(), node.id));
|
||||
backlink.elementChanged(node, newOne);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -275,4 +300,64 @@ public abstract class JSONElementEditor extends Editor {
|
||||
message.revalidate();
|
||||
message.repaint();
|
||||
}
|
||||
|
||||
|
||||
public boolean idChanging() {
|
||||
JSONElement node = (JSONElement) target;
|
||||
List<GameDataElement> toModify = new LinkedList<GameDataElement>();
|
||||
List<GameDataElement> toAlter = new LinkedList<GameDataElement>();
|
||||
for (GameDataElement element : node.getBacklinks()) {
|
||||
GameDataElement activeElement = element;
|
||||
if (element instanceof JSONElement) {
|
||||
activeElement = node.getProject().getGameDataElement((Class<? extends JSONElement>) element.getClass(), element.id);
|
||||
} else if (element instanceof TMXMap) {
|
||||
activeElement = node.getProject().getMap(element.id);
|
||||
} else if (element instanceof WorldmapSegment) {
|
||||
activeElement = node.getProject().getWorldmapSegment(element.id);
|
||||
}
|
||||
if (activeElement.writable) {
|
||||
//No need to alter. Check if we flag a new modification.
|
||||
if (!activeElement.needsSaving()) {
|
||||
toModify.add(activeElement);
|
||||
}
|
||||
} else {
|
||||
toAlter.add(activeElement);
|
||||
}
|
||||
}
|
||||
if (!(toModify.isEmpty() && toAlter.isEmpty())) {
|
||||
IdChangeImpactWizard.Result result = IdChangeImpactWizard.showIdChangeImapctWizard(target, toModify, toAlter);
|
||||
if (result == IdChangeImpactWizard.Result.ok) {
|
||||
for (GameDataElement element : toModify) {
|
||||
element.state = GameDataElement.State.modified;
|
||||
element.childrenChanged(new ArrayList<ProjectTreeNode>());
|
||||
}
|
||||
for (GameDataElement element : toAlter) {
|
||||
if (element instanceof JSONElement) {
|
||||
node.getProject().makeWritable((JSONElement)element);
|
||||
} else if (element instanceof TMXMap) {
|
||||
node.getProject().makeWritable((TMXMap)element);
|
||||
} else if (element instanceof WorldmapSegment) {
|
||||
node.getProject().makeWritable((WorldmapSegment)element);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//setText in cancelIdEdit generates to edit events, one replacing the contents with the empty string, and one with the target.id. We want to skip the first one.
|
||||
public boolean skipNext = false;
|
||||
public void cancelIdEdit(final JTextField idField) {
|
||||
Runnable revertField = new Runnable(){
|
||||
@Override
|
||||
public void run() {
|
||||
skipNext = true;
|
||||
idField.setText(target.id);
|
||||
}
|
||||
};
|
||||
SwingUtilities.invokeLater(revertField);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,12 +166,12 @@ public class NPCEditor extends JSONElementEditor {
|
||||
public void insertFormViewDataField(JPanel pane) {
|
||||
final NPC npc = (NPC) target;
|
||||
|
||||
final FieldUpdateListener listener = new NPCFieldUpdate();
|
||||
final FieldUpdateListener listener = new NPCFieldUpdater();
|
||||
|
||||
npcIcon = createButtonPane(pane, npc.getProject(), npc, NPC.class, npc.getImage(), Spritesheet.Category.monster, listener);
|
||||
|
||||
idField = addTextField(pane, "Internal ID: ", npc.id, npc.writable, listener);
|
||||
nameField = addTextField(pane, "Display name: ", npc.name, npc.writable, listener);
|
||||
nameField = addTranslatableTextField(pane, "Display name: ", npc.name, npc.writable, listener);
|
||||
spawnGroupField = addTextField(pane, "Spawn group ID: ", npc.spawngroup_id, npc.writable, listener);
|
||||
experienceField = addIntegerField(pane, "Experience reward: ", npc.getMonsterExperience(), false, false, listener);
|
||||
dialogueBox = addDialogueBox(pane, npc.getProject(), "Initial phrase: ", npc.dialogue, npc.writable, listener);
|
||||
@@ -504,17 +504,29 @@ public class NPCEditor extends JSONElementEditor {
|
||||
return true;
|
||||
}
|
||||
|
||||
public class NPCFieldUpdate implements FieldUpdateListener {
|
||||
public class NPCFieldUpdater implements FieldUpdateListener {
|
||||
|
||||
@Override
|
||||
public void valueChanged(JComponent source, Object value) {
|
||||
NPC npc = (NPC)target;
|
||||
boolean updateHit = false;
|
||||
if (source == idField) {
|
||||
npc.id = (String) value;
|
||||
NPCEditor.this.name = npc.getDesc();
|
||||
npc.childrenChanged(new ArrayList<ProjectTreeNode>());
|
||||
ATContentStudio.frame.editorChanged(NPCEditor.this);
|
||||
//Events caused by cancel an ID edition. Dismiss.
|
||||
if (skipNext) {
|
||||
skipNext = false;
|
||||
return;
|
||||
}
|
||||
if (target.id.equals((String) value)) return;
|
||||
|
||||
if (idChanging()) {
|
||||
npc.id = (String) value;
|
||||
NPCEditor.this.name = npc.getDesc();
|
||||
npc.childrenChanged(new ArrayList<ProjectTreeNode>());
|
||||
ATContentStudio.frame.editorChanged(NPCEditor.this);
|
||||
} else {
|
||||
cancelIdEdit(idField);
|
||||
return;
|
||||
}
|
||||
} else if (source == nameField) {
|
||||
npc.name = (String) value;
|
||||
NPCEditor.this.name = npc.getDesc();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.gpl.rpg.atcontentstudio.ui.gamedataeditors;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
@@ -8,27 +7,30 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.DefaultListCellRenderer;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.ListModel;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.event.ListDataEvent;
|
||||
import javax.swing.event.ListDataListener;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.swing.event.TableModelEvent;
|
||||
import javax.swing.event.TableModelListener;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import javax.swing.table.TableModel;
|
||||
|
||||
import com.gpl.rpg.atcontentstudio.ATContentStudio;
|
||||
import com.gpl.rpg.atcontentstudio.model.GameDataElement;
|
||||
import com.gpl.rpg.atcontentstudio.model.ProjectTreeNode;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.Quest;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.QuestStage;
|
||||
import com.gpl.rpg.atcontentstudio.ui.CollapsiblePanel;
|
||||
import com.gpl.rpg.atcontentstudio.ui.DefaultIcons;
|
||||
import com.gpl.rpg.atcontentstudio.ui.FieldUpdateListener;
|
||||
import com.gpl.rpg.atcontentstudio.ui.IntegerBasedCheckBox;
|
||||
@@ -43,15 +45,20 @@ public class QuestEditor extends JSONElementEditor {
|
||||
private static final String form_view_id = "Form";
|
||||
private static final String json_view_id = "JSON";
|
||||
|
||||
private QuestStage selectedStage = null;
|
||||
|
||||
private JTextField idField;
|
||||
private JTextField nameField;
|
||||
private IntegerBasedCheckBox visibleBox;
|
||||
private QuestStageTableModel stagesModel;
|
||||
private JTable stagesTable;
|
||||
private JButton createStage;
|
||||
private JButton deleteStage;
|
||||
private JButton moveUp;
|
||||
private JButton moveDown;
|
||||
private StagesListModel stagesListModel;
|
||||
private JList<QuestStage> stagesList;
|
||||
|
||||
// private JPanel stagesParamPane;
|
||||
private JSpinner progressField;
|
||||
private JTextArea logTextField;
|
||||
private JSpinner xpRewardField;
|
||||
private IntegerBasedCheckBox finishQuestBox;
|
||||
|
||||
|
||||
|
||||
public QuestEditor(Quest quest) {
|
||||
@@ -69,255 +76,239 @@ public class QuestEditor extends JSONElementEditor {
|
||||
|
||||
|
||||
idField = addTextField(pane, "Internal ID: ", quest.id, quest.writable, listener);
|
||||
nameField = addTextField(pane, "Quest Name: ", quest.name, quest.writable, listener);
|
||||
nameField = addTranslatableTextField(pane, "Quest Name: ", quest.name, quest.writable, listener);
|
||||
visibleBox = addIntegerBasedCheckBox(pane, "Visible in quest log", quest.visible_in_log, quest.writable, listener);
|
||||
|
||||
JPanel stagesPane = new JPanel();
|
||||
stagesPane.setLayout(new JideBoxLayout(stagesPane, JideBoxLayout.PAGE_AXIS, 6));
|
||||
stagesModel = new QuestStageTableModel(quest, listener);
|
||||
stagesTable = new JTable(stagesModel);
|
||||
stagesTable.getColumnModel().getColumn(0).setMinWidth(100);
|
||||
stagesTable.getColumnModel().getColumn(0).setMaxWidth(100);
|
||||
// stagesTable.getColumnModel().getColumn(1).setPreferredWidth(40);
|
||||
// stagesTable.getColumnModel().getColumn(1).setPreferredWidth(40);
|
||||
stagesTable.getColumnModel().getColumn(2).setMinWidth(100);
|
||||
stagesTable.getColumnModel().getColumn(2).setMaxWidth(100);
|
||||
stagesTable.getColumnModel().getColumn(3).setMinWidth(130);
|
||||
stagesTable.getColumnModel().getColumn(3).setMaxWidth(130);
|
||||
stagesTable.setCellSelectionEnabled(true);
|
||||
stagesTable.getColumnModel().getColumn(1).setCellRenderer(new MultilineCellRenderer());
|
||||
stagesPane.add(new JScrollPane(stagesTable), BorderLayout.CENTER);
|
||||
if (quest.writable) {
|
||||
JPanel buttonPane = new JPanel();
|
||||
buttonPane.setLayout(new JideBoxLayout(buttonPane, JideBoxLayout.LINE_AXIS, 6));
|
||||
createStage = new JButton(new ImageIcon(DefaultIcons.getCreateIcon()));
|
||||
deleteStage = new JButton(new ImageIcon(DefaultIcons.getNullifyIcon()));
|
||||
moveUp = new JButton(new ImageIcon(DefaultIcons.getArrowUpIcon()));
|
||||
moveDown = new JButton(new ImageIcon(DefaultIcons.getArrowDownIcon()));
|
||||
buttonPane.add(createStage, JideBoxLayout.FIX);
|
||||
buttonPane.add(deleteStage, JideBoxLayout.FIX);
|
||||
buttonPane.add(moveUp, JideBoxLayout.FIX);
|
||||
buttonPane.add(moveDown, JideBoxLayout.FIX);
|
||||
buttonPane.add(new JPanel(), JideBoxLayout.VARY);
|
||||
deleteStage.setEnabled(false);
|
||||
moveUp.setEnabled(false);
|
||||
moveDown.setEnabled(false);
|
||||
|
||||
stagesTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
|
||||
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
updateTableButtons();
|
||||
CollapsiblePanel stagesPane = new CollapsiblePanel("Quest stages: ");
|
||||
stagesPane.setLayout(new JideBoxLayout(stagesPane, JideBoxLayout.PAGE_AXIS));
|
||||
stagesListModel = new StagesListModel(quest);
|
||||
stagesList = new JList<QuestStage>(stagesListModel);
|
||||
stagesList.setCellRenderer(new StagesCellRenderer());
|
||||
stagesList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
stagesPane.add(new JScrollPane(stagesList), JideBoxLayout.FIX);
|
||||
final JPanel stagesEditorPane = new JPanel();
|
||||
final JButton createStage = new JButton(new ImageIcon(DefaultIcons.getCreateIcon()));
|
||||
final JButton deleteStage = new JButton(new ImageIcon(DefaultIcons.getNullifyIcon()));
|
||||
final JButton moveStageUp = new JButton(new ImageIcon(DefaultIcons.getArrowUpIcon()));
|
||||
final JButton moveStageDown = new JButton(new ImageIcon(DefaultIcons.getArrowDownIcon()));
|
||||
deleteStage.setEnabled(false);
|
||||
moveStageUp.setEnabled(false);
|
||||
moveStageDown.setEnabled(false);
|
||||
stagesList.addListSelectionListener(new ListSelectionListener() {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
selectedStage = (QuestStage) stagesList.getSelectedValue();
|
||||
if (selectedStage != null) {
|
||||
deleteStage.setEnabled(true);
|
||||
moveStageUp.setEnabled(stagesList.getSelectedIndex() > 0);
|
||||
moveStageDown.setEnabled(stagesList.getSelectedIndex() < (stagesListModel.getSize() - 1));
|
||||
} else {
|
||||
deleteStage.setEnabled(false);
|
||||
moveStageUp.setEnabled(false);
|
||||
moveStageDown.setEnabled(false);
|
||||
}
|
||||
});
|
||||
|
||||
updateStageEditorPane(stagesEditorPane, selectedStage, listener);
|
||||
}
|
||||
});
|
||||
if (quest.writable) {
|
||||
JPanel listButtonsPane = new JPanel();
|
||||
listButtonsPane.setLayout(new JideBoxLayout(listButtonsPane, JideBoxLayout.LINE_AXIS, 6));
|
||||
createStage.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
stagesModel.createStage();
|
||||
listener.valueChanged(stagesTable, null);
|
||||
stagesTable.revalidate();
|
||||
stagesTable.repaint();
|
||||
QuestStage stage = new QuestStage(quest);
|
||||
stagesListModel.addItem(stage);
|
||||
stagesList.setSelectedValue(stage, true);
|
||||
listener.valueChanged(new JLabel(), null); //Item changed, but we took care of it, just do the usual notification and JSON update stuff.
|
||||
}
|
||||
});
|
||||
deleteStage.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
stagesModel.deleteRow(stagesTable.getSelectedRow());
|
||||
listener.valueChanged(stagesTable, null);
|
||||
stagesTable.revalidate();
|
||||
stagesTable.repaint();
|
||||
updateTableButtons();
|
||||
if (selectedStage != null) {
|
||||
stagesListModel.removeItem(selectedStage);
|
||||
selectedStage = null;
|
||||
stagesList.clearSelection();
|
||||
listener.valueChanged(new JLabel(), null); //Item changed, but we took care of it, just do the usual notification and JSON update stuff.
|
||||
}
|
||||
}
|
||||
});
|
||||
moveUp.addActionListener(new ActionListener() {
|
||||
moveStageUp.addActionListener(new ActionListener() {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
stagesModel.moveRow(stagesTable.getSelectedRow(), true);
|
||||
listener.valueChanged(stagesTable, null);
|
||||
stagesTable.setRowSelectionInterval(stagesTable.getSelectedRow() - 1, stagesTable.getSelectedRow() - 1);
|
||||
updateTableButtons();
|
||||
if (selectedStage != null) {
|
||||
stagesListModel.moveUp(selectedStage);
|
||||
stagesList.setSelectedValue(selectedStage, true);
|
||||
listener.valueChanged(new JLabel(), null); //Item changed, but we took care of it, just do the usual notification and JSON update stuff.
|
||||
}
|
||||
}
|
||||
});
|
||||
moveDown.addActionListener(new ActionListener() {
|
||||
moveStageDown.addActionListener(new ActionListener() {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
stagesModel.moveRow(stagesTable.getSelectedRow(), false);
|
||||
listener.valueChanged(stagesTable, null);
|
||||
stagesTable.setRowSelectionInterval(stagesTable.getSelectedRow() + 1, stagesTable.getSelectedRow() + 1);
|
||||
updateTableButtons();
|
||||
if (selectedStage != null) {
|
||||
stagesListModel.moveDown(selectedStage);
|
||||
stagesList.setSelectedValue(selectedStage, true);
|
||||
listener.valueChanged(new JLabel(), null); //Item changed, but we took care of it, just do the usual notification and JSON update stuff.
|
||||
}
|
||||
}
|
||||
});
|
||||
stagesPane.add(buttonPane, JideBoxLayout.FIX);
|
||||
listButtonsPane.add(createStage, JideBoxLayout.FIX);
|
||||
listButtonsPane.add(deleteStage, JideBoxLayout.FIX);
|
||||
listButtonsPane.add(moveStageUp, JideBoxLayout.FIX);
|
||||
listButtonsPane.add(moveStageDown, JideBoxLayout.FIX);
|
||||
listButtonsPane.add(new JPanel(), JideBoxLayout.VARY);
|
||||
stagesPane.add(listButtonsPane, JideBoxLayout.FIX);
|
||||
}
|
||||
if (quest.stages == null || quest.stages.isEmpty()) {
|
||||
stagesPane.collapse();
|
||||
}
|
||||
stagesEditorPane.setLayout(new JideBoxLayout(stagesEditorPane, JideBoxLayout.PAGE_AXIS));
|
||||
stagesPane.add(stagesEditorPane, JideBoxLayout.FIX);
|
||||
pane.add(stagesPane, JideBoxLayout.FIX);
|
||||
|
||||
}
|
||||
|
||||
public void updateTableButtons() {
|
||||
public void updateStageEditorPane(JPanel pane, QuestStage selectedStage, FieldUpdateListener listener) {
|
||||
pane.removeAll();
|
||||
if (selectedStage != null) {
|
||||
boolean writable = ((Quest)target).writable;
|
||||
progressField = addIntegerField(pane, "Progress ID: ", selectedStage.progress, false, writable, listener);
|
||||
logTextField = addTranslatableTextArea(pane, "Log text: ", selectedStage.log_text, writable, listener);
|
||||
xpRewardField = addIntegerField(pane, "XP Reward: ", selectedStage.exp_reward, false, writable, listener);
|
||||
finishQuestBox = addIntegerBasedCheckBox(pane, "Finishes quest", selectedStage.finishes_quest, writable, listener);
|
||||
addBacklinksList(pane, selectedStage, "Elements linking to this quest stage");
|
||||
|
||||
if (stagesTable.getSelectedRow() >= 0 && stagesTable.getSelectedRow() < stagesModel.getRowCount()) {
|
||||
deleteStage.setEnabled(true);
|
||||
if (stagesTable.getSelectedRow() == 0) {
|
||||
moveUp.setEnabled(false);
|
||||
} else {
|
||||
moveUp.setEnabled(true);
|
||||
}
|
||||
if (stagesTable.getSelectedRow() >= stagesModel.getRowCount() - 1) {
|
||||
moveDown.setEnabled(false);
|
||||
} else {
|
||||
moveDown.setEnabled(true);
|
||||
}
|
||||
} else {
|
||||
deleteStage.setEnabled(false);
|
||||
moveUp.setEnabled(false);
|
||||
moveDown.setEnabled(false);
|
||||
}
|
||||
|
||||
pane.revalidate();
|
||||
pane.repaint();
|
||||
}
|
||||
|
||||
public class QuestStageTableModel implements TableModel {
|
||||
public static class StagesListModel implements ListModel<QuestStage> {
|
||||
|
||||
Quest quest;
|
||||
FieldUpdateListener listener;
|
||||
|
||||
public QuestStageTableModel(Quest q, FieldUpdateListener listener) {
|
||||
this.quest = q;
|
||||
this.listener = listener;
|
||||
Quest source;
|
||||
|
||||
public StagesListModel(Quest quest) {
|
||||
this.source = quest;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
if (quest.stages == null) return 0;
|
||||
return quest.stages.size();
|
||||
public int getSize() {
|
||||
if (source.stages == null) return 0;
|
||||
return source.stages.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return 4;
|
||||
public QuestStage getElementAt(int index) {
|
||||
if (source.stages == null) return null;
|
||||
return source.stages.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int columnIndex) {
|
||||
switch (columnIndex) {
|
||||
case 0:
|
||||
return "Progress ID";
|
||||
case 1:
|
||||
return "Log text";
|
||||
case 2:
|
||||
return "XP reward";
|
||||
case 3:
|
||||
return "Finishes quest";
|
||||
default:
|
||||
return "???";
|
||||
|
||||
public void addItem(QuestStage item) {
|
||||
if (source.stages == null) {
|
||||
source.stages = new ArrayList<QuestStage>();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex) {
|
||||
switch (columnIndex) {
|
||||
case 0:
|
||||
return Integer.class;
|
||||
case 1:
|
||||
return String.class;
|
||||
case 2:
|
||||
return Integer.class;
|
||||
case 3:
|
||||
return Boolean.class;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
||||
return quest.writable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
switch (columnIndex) {
|
||||
case 0:
|
||||
return quest.stages.get(rowIndex).progress;
|
||||
case 1:
|
||||
return quest.stages.get(rowIndex).log_text;
|
||||
case 2:
|
||||
return quest.stages.get(rowIndex).exp_reward;
|
||||
case 3:
|
||||
return quest.stages.get(rowIndex).finishes_quest != null && quest.stages.get(rowIndex).finishes_quest.equals(1);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
||||
switch (columnIndex) {
|
||||
case 0:
|
||||
quest.stages.get(rowIndex).progress = (Integer)aValue;
|
||||
break;
|
||||
case 1:
|
||||
quest.stages.get(rowIndex).log_text = (String)aValue;
|
||||
break;
|
||||
case 2:
|
||||
quest.stages.get(rowIndex).exp_reward = (Integer)aValue;
|
||||
break;
|
||||
case 3:
|
||||
quest.stages.get(rowIndex).finishes_quest = ((Boolean)aValue) ? one : null;
|
||||
break;
|
||||
}
|
||||
listener.valueChanged(stagesTable, aValue);
|
||||
}
|
||||
|
||||
public void createStage() {
|
||||
if (quest.stages == null) quest.stages = new ArrayList<Quest.QuestStage>();
|
||||
quest.stages.add(new Quest.QuestStage());
|
||||
for (TableModelListener l: listeners) {
|
||||
l.tableChanged(new TableModelEvent(this, quest.stages.size() - 1));
|
||||
source.stages.add(item);
|
||||
int index = source.stages.indexOf(item);
|
||||
for (ListDataListener l : listeners) {
|
||||
l.intervalAdded(new ListDataEvent(this, ListDataEvent.INTERVAL_ADDED, index, index));
|
||||
}
|
||||
}
|
||||
|
||||
public void moveRow(int rowNumber, boolean moveUp) {
|
||||
Quest.QuestStage stage = quest.stages.get(rowNumber);
|
||||
quest.stages.remove(stage);
|
||||
quest.stages.add(rowNumber + (moveUp ? -1 : 1), stage);
|
||||
for (TableModelListener l : listeners) {
|
||||
l.tableChanged(new TableModelEvent(this, rowNumber + (moveUp ? -1 : 0), rowNumber + (moveUp ? 0 : 1)));
|
||||
public void removeItem(QuestStage item) {
|
||||
int index = source.stages.indexOf(item);
|
||||
source.stages.remove(item);
|
||||
if (source.stages.isEmpty()) {
|
||||
source.stages = null;
|
||||
}
|
||||
for (ListDataListener l : listeners) {
|
||||
l.intervalRemoved(new ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, index, index));
|
||||
}
|
||||
}
|
||||
|
||||
public void itemChanged(QuestStage item) {
|
||||
int index = source.stages.indexOf(item);
|
||||
for (ListDataListener l : listeners) {
|
||||
l.contentsChanged(new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, index, index));
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteRow(int rowNumber) {
|
||||
quest.stages.remove(rowNumber);
|
||||
for (TableModelListener l: listeners) {
|
||||
l.tableChanged(new TableModelEvent(this, rowNumber, quest.stages.size()));
|
||||
public void moveUp(QuestStage item) {
|
||||
int index = source.stages.indexOf(item);
|
||||
QuestStage exchanged = source.stages.get(index - 1);
|
||||
source.stages.set(index, exchanged);
|
||||
source.stages.set(index - 1, item);
|
||||
for (ListDataListener l : listeners) {
|
||||
l.contentsChanged(new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, index - 1, index));
|
||||
}
|
||||
if (quest.stages.isEmpty()) quest.stages = null;
|
||||
}
|
||||
|
||||
public List<TableModelListener> listeners = new CopyOnWriteArrayList<TableModelListener>();
|
||||
|
||||
|
||||
public void moveDown(QuestStage item) {
|
||||
int index = source.stages.indexOf(item);
|
||||
QuestStage exchanged = source.stages.get(index + 1);
|
||||
source.stages.set(index, exchanged);
|
||||
source.stages.set(index + 1, item);
|
||||
for (ListDataListener l : listeners) {
|
||||
l.contentsChanged(new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, index, index + 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
List<ListDataListener> listeners = new CopyOnWriteArrayList<ListDataListener>();
|
||||
|
||||
@Override
|
||||
public void addTableModelListener(TableModelListener l) {
|
||||
public void addListDataListener(ListDataListener l) {
|
||||
listeners.add(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTableModelListener(TableModelListener l) {
|
||||
public void removeListDataListener(ListDataListener l) {
|
||||
listeners.remove(l);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static class StagesCellRenderer extends DefaultListCellRenderer {
|
||||
private static final long serialVersionUID = 7987880146189575234L;
|
||||
|
||||
@Override
|
||||
public Component getListCellRendererComponent(@SuppressWarnings("rawtypes") JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||
Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||
if (c instanceof JLabel) {
|
||||
JLabel label = ((JLabel)c);
|
||||
label.setText(((QuestStage)value).getDesc());
|
||||
label.setIcon(new ImageIcon(((QuestStage)value).getIcon()));
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class QuestFieldUpdater implements FieldUpdateListener {
|
||||
|
||||
@Override
|
||||
public void valueChanged(JComponent source, Object value) {
|
||||
Quest quest = (Quest) target;
|
||||
if (source == idField) {
|
||||
quest.id = (String) value;
|
||||
QuestEditor.this.name = quest.getDesc();
|
||||
quest.childrenChanged(new ArrayList<ProjectTreeNode>());
|
||||
ATContentStudio.frame.editorChanged(QuestEditor.this);
|
||||
//Events caused by cancel an ID edition. Dismiss.
|
||||
if (skipNext) {
|
||||
skipNext = false;
|
||||
return;
|
||||
}
|
||||
if (target.id.equals((String) value)) return;
|
||||
|
||||
if (idChanging()) {
|
||||
quest.id = (String) value;
|
||||
QuestEditor.this.name = quest.getDesc();
|
||||
quest.childrenChanged(new ArrayList<ProjectTreeNode>());
|
||||
ATContentStudio.frame.editorChanged(QuestEditor.this);
|
||||
} else {
|
||||
cancelIdEdit(idField);
|
||||
return;
|
||||
}
|
||||
} else if (source == nameField) {
|
||||
quest.name = (String) value;
|
||||
QuestEditor.this.name = quest.getDesc();
|
||||
@@ -325,6 +316,18 @@ public class QuestEditor extends JSONElementEditor {
|
||||
ATContentStudio.frame.editorChanged(QuestEditor.this);
|
||||
} else if (source == visibleBox) {
|
||||
quest.visible_in_log = (Integer) value;
|
||||
} else if (source == progressField) {
|
||||
selectedStage.progress = (Integer) value;
|
||||
stagesListModel.itemChanged(selectedStage);
|
||||
} else if (source == logTextField) {
|
||||
selectedStage.log_text = (String) value;
|
||||
stagesListModel.itemChanged(selectedStage);
|
||||
} else if (source == xpRewardField) {
|
||||
selectedStage.exp_reward = (Integer) value;
|
||||
stagesListModel.itemChanged(selectedStage);
|
||||
} else if (source == finishQuestBox) {
|
||||
selectedStage.finishes_quest = (Integer) value;
|
||||
stagesListModel.itemChanged(selectedStage);
|
||||
}
|
||||
|
||||
|
||||
@@ -339,47 +342,6 @@ public class QuestEditor extends JSONElementEditor {
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class MultilineCellRenderer extends JTextArea implements TableCellRenderer {
|
||||
private static final long serialVersionUID = 6539816623608859506L;
|
||||
|
||||
public MultilineCellRenderer() {
|
||||
setLineWrap(true);
|
||||
setWrapStyleWord(true);
|
||||
//setOpaque(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table,
|
||||
Object value, boolean isSelected, boolean hasFocus,
|
||||
int row, int column) {
|
||||
if (isSelected) {
|
||||
setForeground(stagesTable.getSelectionForeground());
|
||||
setBackground(stagesTable.getSelectionBackground());
|
||||
} else {
|
||||
setForeground(stagesTable.getForeground());
|
||||
setBackground(stagesTable.getBackground());
|
||||
}
|
||||
setFont(stagesTable.getFont());
|
||||
if (hasFocus) {
|
||||
setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
|
||||
if (stagesTable.isCellEditable(row, column)) {
|
||||
setForeground(UIManager.getColor("Table.focusCellForeground"));
|
||||
setBackground(UIManager.getColor("Table.focusCellBackground"));
|
||||
}
|
||||
} else {
|
||||
setBorder(BorderFactory.createLineBorder(getBackground(), 1));
|
||||
}
|
||||
setText((value == null ? "" : value.toString()));
|
||||
|
||||
int fh = getFontMetrics(getFont()).getHeight();
|
||||
// int tl = getText().length();
|
||||
setSize(stagesTable.getWidth(), fh);
|
||||
stagesTable.setRowHeight(row, getPreferredSize().height);
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import javax.swing.ImageIcon;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JToolTip;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.ToolTipManager;
|
||||
|
||||
import prefuse.Display;
|
||||
@@ -68,6 +69,7 @@ public class DialogueGraphView extends Display {
|
||||
public static final String ICON = "icon";
|
||||
public static final String TARGET = "target";
|
||||
public static final String REPLY = "reply";
|
||||
public static final String HIDDEN_REPLY = "hidden_reply";
|
||||
public static final String HAS_REQS = "has_reqs";
|
||||
|
||||
private static final Schema DECORATOR_SCHEMA = PrefuseLib.getVisualItemSchema();
|
||||
@@ -174,6 +176,7 @@ public class DialogueGraphView extends Display {
|
||||
graph.addColumn(ICON, Image.class, DefaultIcons.getNullifyIcon());
|
||||
graph.addColumn(TARGET, GameDataElement.class, null);
|
||||
graph.addColumn(REPLY, Dialogue.Reply.class, null);
|
||||
graph.addColumn(HIDDEN_REPLY, Dialogue.Reply.class, null);
|
||||
graph.addColumn(HAS_REQS, boolean.class, false);
|
||||
addDialogue(dialogue, npcIcon);
|
||||
}
|
||||
@@ -224,6 +227,8 @@ public class DialogueGraphView extends Display {
|
||||
} else if (r.next_phrase != null) {
|
||||
//Go directly to next phrase
|
||||
rNode = addDialogue(r.next_phrase, npcIcon);
|
||||
//Add a pointer to the hidden reply, in order to fetch requirements later.
|
||||
rNode.set(HIDDEN_REPLY, r);
|
||||
} else if (Dialogue.Reply.KEY_PHRASE_ID.contains(r.next_phrase_id)) {
|
||||
//Go directly to key phrase
|
||||
rNode = addKeyPhraseNode(d, r.next_phrase_id);
|
||||
@@ -499,27 +504,32 @@ public class DialogueGraphView extends Display {
|
||||
label = new JLabel(new ImageIcon(DefaultIcons.getDialogueIcon()));
|
||||
label.setText(d.id);
|
||||
content.add(label, JideBoxLayout.FIX);
|
||||
if (tooltippedItem.get(REPLY) == null) {
|
||||
if (d.rewards != null && !d.rewards.isEmpty()) {
|
||||
for (Dialogue.Reward r : d.rewards) {
|
||||
label = new JLabel();
|
||||
DialogueEditor.decorateRewardJLabel(label, r);
|
||||
Object replObj = tooltippedItem.get(REPLY);
|
||||
if (replObj == null) {
|
||||
replObj = tooltippedItem.get(HIDDEN_REPLY);
|
||||
}
|
||||
if (replObj != null && replObj instanceof Dialogue.Reply) {
|
||||
Dialogue.Reply r = (Dialogue.Reply) replObj;
|
||||
if (r.requirements != null && !r.requirements.isEmpty()) {
|
||||
JLabel reqTitle = new JLabel("--Requirements--", SwingConstants.CENTER);
|
||||
content.add(reqTitle, JideBoxLayout.FIX);
|
||||
for (Requirement req : r.requirements) {
|
||||
label = new JLabel("", SwingConstants.CENTER);
|
||||
DialogueEditor.decorateRequirementJLabel(label, req);
|
||||
content.add(label, JideBoxLayout.FIX);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Object replObj = tooltippedItem.get(REPLY);
|
||||
if (replObj instanceof Dialogue.Reply) {
|
||||
Dialogue.Reply r = (Dialogue.Reply) replObj;
|
||||
if (r.requirements != null && !r.requirements.isEmpty()) {
|
||||
for (Requirement req : r.requirements) {
|
||||
label = new JLabel();
|
||||
DialogueEditor.decorateRequirementJLabel(label, req);
|
||||
content.add(label, JideBoxLayout.FIX);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (d.rewards != null && !d.rewards.isEmpty()) {
|
||||
JLabel rewTitle = new JLabel("--Rewards--", SwingConstants.CENTER);
|
||||
rewTitle.setAlignmentY(CENTER_ALIGNMENT);
|
||||
content.add(rewTitle, JideBoxLayout.FIX);
|
||||
for (Dialogue.Reward r : d.rewards) {
|
||||
label = new JLabel("", SwingConstants.CENTER);
|
||||
DialogueEditor.decorateRewardJLabel(label, r);
|
||||
content.add(label, JideBoxLayout.FIX);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ import com.gpl.rpg.atcontentstudio.model.gamedata.Item;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.JSONElement;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.NPC;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.Quest;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.QuestStage;
|
||||
import com.gpl.rpg.atcontentstudio.model.gamedata.Requirement;
|
||||
import com.gpl.rpg.atcontentstudio.model.maps.ContainerArea;
|
||||
import com.gpl.rpg.atcontentstudio.model.maps.KeyArea;
|
||||
@@ -131,7 +132,7 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
|
||||
|
||||
private JPanel layerDetailsPane;
|
||||
private BooleanBasedCheckBox layerVisibleBox;
|
||||
//private BooleanBasedCheckBox activeLayerBox;
|
||||
private JCheckBox groupActiveForNewGame;
|
||||
private JTextField layerNameField;
|
||||
private MapObjectsListModel groupObjectsListModel;
|
||||
@SuppressWarnings("rawtypes")
|
||||
@@ -160,7 +161,7 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
|
||||
@SuppressWarnings("rawtypes")
|
||||
private JComboBox evaluateTriggerBox;
|
||||
private JSpinner quantityField;
|
||||
private JCheckBox activeForNewGame;
|
||||
private JCheckBox spawnActiveForNewGame;
|
||||
private JTextField spawngroupField;
|
||||
@SuppressWarnings("rawtypes")
|
||||
private JList npcList;
|
||||
@@ -172,7 +173,7 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
|
||||
@SuppressWarnings("rawtypes")
|
||||
private JComboBox requirementObj;
|
||||
private JTextField requirementObjId;
|
||||
private JSpinner requirementValue;
|
||||
private JComponent requirementValue;
|
||||
private BooleanBasedCheckBox requirementNegated;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@@ -332,7 +333,7 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
|
||||
break;
|
||||
}
|
||||
}
|
||||
activeForNewGame = addBooleanBasedCheckBox(groupDetailPane, "Active for new game", objGroup.active, map.writable, listener);
|
||||
groupActiveForNewGame = addBooleanBasedCheckBox(groupDetailPane, "Active for new game", objGroup.active, map.writable, listener);
|
||||
groupObjectsListModel = new MapObjectsListModel(objGroup);
|
||||
groupObjectsList = new JList(groupObjectsListModel);
|
||||
groupObjectsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
@@ -576,7 +577,7 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
|
||||
areaField = addTextField(pane, "Spawn area ID: ", ((SpawnArea)selected).name, ((TMXMap)target).writable, listener);
|
||||
spawngroupField = addTextField(pane, "Spawn group ID: ", ((SpawnArea)selected).spawngroup_id, ((TMXMap)target).writable, listener);
|
||||
quantityField = addIntegerField(pane, "Number of spawned NPCs: ", ((SpawnArea)selected).quantity, false, ((TMXMap)target).writable, listener);
|
||||
activeForNewGame = addBooleanBasedCheckBox(pane, "Active in a new game: ", ((SpawnArea)selected).active, ((TMXMap)target).writable, listener);
|
||||
spawnActiveForNewGame = addBooleanBasedCheckBox(pane, "Active in a new game: ", ((SpawnArea)selected).active, ((TMXMap)target).writable, listener);
|
||||
npcListModel = new SpawnGroupNpcListModel((SpawnArea) selected);
|
||||
npcList = new JList(npcListModel);
|
||||
npcList.setCellRenderer(new GDERenderer(true, ((TMXMap)target).writable));
|
||||
@@ -642,7 +643,7 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
|
||||
case questProgress:
|
||||
requirementObj = addQuestBox(pane, project, "Quest: ", (Quest) requirement.required_obj, writable, listener);
|
||||
requirementObjId = null;
|
||||
requirementValue = addIntegerField(pane, "Quest stage: ", requirement.required_value, false, writable, listener);
|
||||
requirementValue = addQuestStageBox(pane, project, "Quest stage: ", requirement.required_value, writable, listener, (Quest) requirement.required_obj, requirementObj);
|
||||
break;
|
||||
case skillLevel:
|
||||
requirementObj = null;
|
||||
@@ -1615,7 +1616,7 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
|
||||
gdeIcon.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (map.state == GameDataElement.State.modified || map.state == GameDataElement.State.created) {
|
||||
if (map.needsSaving()) {
|
||||
int confirm = JOptionPane.showConfirmDialog(TMXMapEditor.this, "You have unsaved changes in ATCS.\nYou'd better save your changes in ATCS before opening this map with the external editor.\nDo you want to save before opening the file?", "Save before opening?", JOptionPane.YES_NO_CANCEL_OPTION);
|
||||
if (confirm == JOptionPane.CANCEL_OPTION) return;
|
||||
if (confirm == JOptionPane.YES_OPTION) {
|
||||
@@ -1633,7 +1634,7 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (map.state == GameDataElement.State.modified) {
|
||||
if (map.needsSaving()) {
|
||||
int confirm = JOptionPane.showConfirmDialog(TMXMapEditor.this, "You modified this map in ATCS. All ATCS-made changes will be lost if you confirm.\n On the other hand, if you save using ATCS, all external changes will be lost.\n Do you want to reload?", "Confirm reload?", JOptionPane.OK_CANCEL_OPTION);
|
||||
if (confirm == JOptionPane.CANCEL_OPTION) return;
|
||||
}
|
||||
@@ -1775,9 +1776,9 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
|
||||
modified = false;
|
||||
tmxViewer.revalidate();
|
||||
tmxViewer.repaint();
|
||||
} else if (source == activeForNewGame) {
|
||||
} else if (source == groupActiveForNewGame) {
|
||||
if (selectedLayer instanceof tiled.core.ObjectGroup) {
|
||||
map.getGroup((tiled.core.ObjectGroup) selectedLayer).active = activeForNewGame.isSelected();
|
||||
map.getGroup((tiled.core.ObjectGroup) selectedLayer).active = groupActiveForNewGame.isSelected();
|
||||
}
|
||||
modified = true;
|
||||
} else if (source == layerList) {
|
||||
@@ -1904,7 +1905,7 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
|
||||
SpawnArea area = (SpawnArea) selectedMapObject;
|
||||
area.quantity = (Integer) value;
|
||||
}
|
||||
} else if (source == activeForNewGame) {
|
||||
} else if (source == spawnActiveForNewGame) {
|
||||
if (selectedMapObject instanceof SpawnArea) {
|
||||
SpawnArea area = (SpawnArea) selectedMapObject;
|
||||
area.active = (Boolean) value;
|
||||
@@ -1949,11 +1950,37 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
|
||||
} else if (source == requirementValue) {
|
||||
if (selectedMapObject instanceof KeyArea) {
|
||||
KeyArea area = (KeyArea) selectedMapObject;
|
||||
Quest quest = null;
|
||||
QuestStage stage = null;
|
||||
if (requirementValue instanceof JComboBox<?>) {
|
||||
quest = ((Quest)area.requirement.required_obj);
|
||||
if (quest != null && area.requirement.required_value != null) {
|
||||
stage = quest.getStage(area.requirement.required_value);
|
||||
if (stage != null) stage.removeBacklink(map);
|
||||
}
|
||||
}
|
||||
area.requirement.required_value = (Integer) value;
|
||||
if (quest != null) {
|
||||
stage = quest.getStage(area.requirement.required_value);
|
||||
if (stage != null) stage.addBacklink(map);
|
||||
}
|
||||
if (area.oldSchoolRequirement) area.updateNameFromRequirementChange();
|
||||
} else if (selectedMapObject instanceof ReplaceArea) {
|
||||
ReplaceArea area = (ReplaceArea) selectedMapObject;
|
||||
Quest quest = null;
|
||||
QuestStage stage = null;
|
||||
if (requirementValue instanceof JComboBox<?>) {
|
||||
quest = ((Quest)area.requirement.required_obj);
|
||||
if (quest != null && area.requirement.required_value != null) {
|
||||
stage = quest.getStage(area.requirement.required_value);
|
||||
if (stage != null) stage.removeBacklink(map);
|
||||
}
|
||||
}
|
||||
area.requirement.required_value = (Integer) value;
|
||||
if (quest != null) {
|
||||
stage = quest.getStage(area.requirement.required_value);
|
||||
if (stage != null) stage.addBacklink(map);
|
||||
}
|
||||
if (area.oldSchoolRequirement) area.updateNameFromRequirementChange();
|
||||
}
|
||||
} else if (source == requirementNegated) {
|
||||
|
||||
@@ -63,7 +63,8 @@ public class WorldMapEditor extends Editor {
|
||||
|
||||
public String mapBeingAddedID = null;
|
||||
public String selectedLabel = null;
|
||||
|
||||
WorldMapView mapView = null;
|
||||
|
||||
public WorldMapEditor(WorldmapSegment worldmap) {
|
||||
target = worldmap;
|
||||
this.name = worldmap.id;
|
||||
@@ -114,7 +115,7 @@ public class WorldMapEditor extends Editor {
|
||||
addLabelField(pane, "Worldmap File: ", ((Worldmap)worldmap.getParent()).worldmapFile.getAbsolutePath());
|
||||
pane.add(createButtonPane(worldmap), JideBoxLayout.FIX);
|
||||
|
||||
final WorldMapView mapView = new WorldMapView(worldmap);
|
||||
mapView = new WorldMapView(worldmap);
|
||||
JScrollPane mapScroller = new JScrollPane(mapView);
|
||||
final JViewport vPort = mapScroller.getViewport();
|
||||
|
||||
@@ -296,6 +297,7 @@ public class WorldMapEditor extends Editor {
|
||||
for (String s : mapView.selected) {
|
||||
map.labelledMaps.get(selectedLabel).add(s);
|
||||
}
|
||||
notifyModelModified();
|
||||
mapView.revalidate();
|
||||
mapView.repaint();
|
||||
}
|
||||
@@ -319,6 +321,7 @@ public class WorldMapEditor extends Editor {
|
||||
worldmap.labels.put(created.id, created);
|
||||
worldmap.labels.remove(selectedLabel);
|
||||
selectedLabel = created.id;
|
||||
notifyModelModified();
|
||||
mapView.revalidate();
|
||||
mapView.repaint();
|
||||
}
|
||||
@@ -334,6 +337,7 @@ public class WorldMapEditor extends Editor {
|
||||
worldmap.labelledMaps.remove(selectedLabel);
|
||||
worldmap.labels.remove(selectedLabel);
|
||||
selectedLabel = null;
|
||||
notifyModelModified();
|
||||
mapView.revalidate();
|
||||
mapView.repaint();
|
||||
}
|
||||
@@ -348,6 +352,7 @@ public class WorldMapEditor extends Editor {
|
||||
public void labelCreated(NamedArea created) {
|
||||
worldmap.labelledMaps.put(created.id, new ArrayList<String>());
|
||||
worldmap.labelledMaps.get(created.id).addAll(mapView.selected);
|
||||
notifyModelModified();
|
||||
mapView.revalidate();
|
||||
mapView.repaint();
|
||||
}
|
||||
@@ -436,11 +441,12 @@ public class WorldMapEditor extends Editor {
|
||||
mapView.selected.remove(selectedMap);
|
||||
mapSelectionChanged();
|
||||
mapView.updateFromModel();
|
||||
notifyModelModified();
|
||||
update = true;
|
||||
} else if (editMode == EditMode.addMap && mapBeingAddedID != null) {
|
||||
if (e.getButton() == MouseEvent.BUTTON1) {
|
||||
mapView.recomputeSize();
|
||||
mapView.pushToModel();
|
||||
pushToModel();
|
||||
}
|
||||
mapView.updateFromModel();
|
||||
update = true;
|
||||
@@ -463,7 +469,7 @@ public class WorldMapEditor extends Editor {
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
dragStart = null;
|
||||
if (editMode == EditMode.moveMaps) {
|
||||
mapView.pushToModel();
|
||||
pushToModel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -713,4 +719,16 @@ public class WorldMapEditor extends Editor {
|
||||
message.repaint();
|
||||
}
|
||||
|
||||
public void pushToModel() {
|
||||
mapView.pushToModel();
|
||||
notifyModelModified();
|
||||
}
|
||||
|
||||
public void notifyModelModified() {
|
||||
target.state = GameDataElement.State.modified;
|
||||
this.name = ((WorldmapSegment)target).getDesc();
|
||||
target.childrenChanged(new ArrayList<ProjectTreeNode>());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -35,13 +35,26 @@ public class SpriteChooser extends JDialog {
|
||||
private static final int MAX_PER_ROW = 10;
|
||||
|
||||
public static Map<Project, Map<Spritesheet.Category, SpriteChooser>> cache = new LinkedHashMap<Project, Map<Spritesheet.Category,SpriteChooser>>();
|
||||
|
||||
public static Map<Project, Map<Spritesheet.Category, List<Spritesheet>>> cacheValidator = new LinkedHashMap<Project, Map<Category,List<Spritesheet>>>();
|
||||
|
||||
|
||||
public static SpriteChooser getChooser(Project proj, Spritesheet.Category category) {
|
||||
if (cache.get(proj) == null) {
|
||||
cache.put(proj, new LinkedHashMap<Spritesheet.Category, SpriteChooser>());
|
||||
}
|
||||
if (cache.get(proj).get(category) == null) {
|
||||
cache.get(proj).put(category, new SpriteChooser(proj, category));
|
||||
} else {
|
||||
List<Spritesheet> spritesheets = new ArrayList<Spritesheet>();
|
||||
for (int i=0; i<proj.getSpritesheetCount(); i++) {
|
||||
Spritesheet sheet = proj.getSpritesheet(i);
|
||||
if (sheet.category == category) {
|
||||
spritesheets.add(sheet);
|
||||
}
|
||||
}
|
||||
if ( !spritesheets.equals(cacheValidator.get(proj).get(category)) ) {
|
||||
cache.get(proj).put(category, new SpriteChooser(proj, category));
|
||||
}
|
||||
}
|
||||
SpriteChooser wanted = cache.get(proj).get(category);
|
||||
wanted.group.clearSelection();
|
||||
@@ -65,11 +78,16 @@ public class SpriteChooser extends JDialog {
|
||||
setTitle("Select a sprite");
|
||||
setModalityType(ModalityType.APPLICATION_MODAL);
|
||||
List<Spritesheet> spritesheets = new ArrayList<Spritesheet>();
|
||||
for (Spritesheet sheet : proj.baseContent.gameSprites.spritesheets) {
|
||||
for (int i=0; i<proj.getSpritesheetCount(); i++) {
|
||||
Spritesheet sheet = proj.getSpritesheet(i);
|
||||
if (sheet.category == category) {
|
||||
spritesheets.add(sheet);
|
||||
}
|
||||
}
|
||||
if (cacheValidator.get(proj) == null) {
|
||||
cacheValidator.put(proj, new LinkedHashMap<Spritesheet.Category, List<Spritesheet>>());
|
||||
}
|
||||
cacheValidator.get(proj).put(category, spritesheets);
|
||||
|
||||
|
||||
JPanel pane = new JPanel();
|
||||
|
||||
@@ -445,6 +445,7 @@ public class SpritesheetEditor extends Editor {
|
||||
} else if (source == categoryBox) {
|
||||
sheet.category = (Spritesheet.Category) value;
|
||||
}
|
||||
sheet.save();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.gpl.rpg.atcontentstudio.ui.tools;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.gpl.rpg.atcontentstudio.ui.tools;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
30
src/com/gpl/rpg/atcontentstudio/utils/HashUtils.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package com.gpl.rpg.atcontentstudio.utils;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.zackehh.siphash.SipHash;
|
||||
import com.zackehh.siphash.SipHashResult;
|
||||
|
||||
public class HashUtils {
|
||||
|
||||
private static final Map<String, SipHash> HASHER_CACHE = new LinkedHashMap<String, SipHash>();
|
||||
|
||||
static String siphash(String key, byte[] data) {
|
||||
SipHash hasher = HASHER_CACHE.get(key);
|
||||
if (hasher == null) {
|
||||
hasher= new SipHash("Weblate Sip Hash".getBytes());
|
||||
HASHER_CACHE.put(key, hasher);
|
||||
}
|
||||
|
||||
if (data != null) {
|
||||
SipHashResult result = hasher.hash(data);
|
||||
return result.getHex();
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
103
src/com/gpl/rpg/atcontentstudio/utils/WeblateIntegration.java
Normal file
@@ -0,0 +1,103 @@
|
||||
package com.gpl.rpg.atcontentstudio.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
|
||||
import com.gpl.rpg.atcontentstudio.model.Workspace;
|
||||
import com.gpl.rpg.atcontentstudio.utils.WeblateIntegration.WeblateTranslationUnit.Status;
|
||||
|
||||
public class WeblateIntegration {
|
||||
|
||||
static final String WEBLATE_SIPASH_KEY = "Weblate Sip Hash";
|
||||
|
||||
public static String weblateHash(String str, String ctx) {
|
||||
|
||||
byte[] data = null;
|
||||
|
||||
if (str != null) {
|
||||
byte[] strBytes;
|
||||
try {
|
||||
strBytes = str.getBytes("UTF-8");
|
||||
byte[] ctxBytes = ctx.getBytes("UTF-8");
|
||||
data = new byte[strBytes.length + ctxBytes.length];
|
||||
System.arraycopy(strBytes, 0, data, 0, strBytes.length);
|
||||
System.arraycopy(ctxBytes, 0, data, strBytes.length, ctxBytes.length);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
} else {
|
||||
try {
|
||||
data = ctx.getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return HashUtils.siphash(WEBLATE_SIPASH_KEY, data);
|
||||
}
|
||||
|
||||
public static String getWeblateLabelURI(String text) {
|
||||
return "https://hosted.weblate.org/translate/andors-trail/game-content/"+Workspace.activeWorkspace.settings.translatorLanguage.getCurrentValue()+"/?checksum="+weblateHash(text, "");
|
||||
}
|
||||
|
||||
public static class WeblateTranslationUnit {
|
||||
public enum Status {
|
||||
notAllowed, error, absent, notTranslated, warning, fuzzy, done
|
||||
}
|
||||
|
||||
public Status status;
|
||||
public String translatedText;
|
||||
}
|
||||
|
||||
public static WeblateTranslationUnit getTranslationUnit(String text) {
|
||||
WeblateTranslationUnit unit = new WeblateTranslationUnit();
|
||||
if (!Workspace.activeWorkspace.settings.useInternet.getCurrentValue()) {
|
||||
unit.status = Status.notAllowed;
|
||||
unit.translatedText = "Allow internet connection in the workspace settings to get translation status";
|
||||
} else {
|
||||
unit.status = Status.absent;
|
||||
unit.translatedText = "Cannot find this on weblate";
|
||||
String hash = weblateHash(text, "");
|
||||
try {
|
||||
Document wlDoc = Jsoup.connect(getWeblateLabelURI(text)).get();
|
||||
Element textArea = wlDoc.getElementById("id_"+hash+"_0");
|
||||
if (textArea != null) {
|
||||
String trans = textArea.text();
|
||||
if (trans != null) {
|
||||
unit.translatedText = trans.trim();
|
||||
if (unit.translatedText.isEmpty()) {
|
||||
unit.translatedText = "Not yet translated";
|
||||
unit.status = Status.notTranslated;
|
||||
} else {
|
||||
unit.status = Status.done;
|
||||
}
|
||||
}
|
||||
Element fuzzyBox = wlDoc.getElementById("id_"+hash+"_fuzzy");
|
||||
if (fuzzyBox != null && fuzzyBox.hasAttr("checked")) {
|
||||
if ("checked".equals(fuzzyBox.attr("checked"))) {
|
||||
unit.status = Status.fuzzy;
|
||||
}
|
||||
} else {
|
||||
Elements dangerZone = wlDoc.getElementsByAttributeValue("class", "panel panel-danger");
|
||||
if (dangerZone != null && !dangerZone.isEmpty()) {
|
||||
unit.status = Status.warning;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
unit.status = Status.error;
|
||||
unit.translatedText = "Cannot connect to weblate: "+e.getMessage();
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return unit;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||