Compare commits

...

69 Commits

Author SHA1 Message Date
Zukero
75d6f8e98f v0.6.11 released. 2018-03-04 15:43:45 +01:00
Zukero
38c206cbaf TMX Maps editor's "Replacement" tab is now called "Testing" and has more
tools.
2018-03-04 15:39:30 +01:00
Zukero
b12ed1802f Added hero sprite rendering to help debug layering issues in
"Replacements" tab of TMX Map editor.
2018-03-04 12:19:56 +01:00
Zukero
0b8bc8448a Fixed memory leaks.
Closed projects were held in memory because the Map folder watcher
threads were still alive.
2018-02-28 16:25:28 +01:00
Zukero
221a031c2b Added support for new spawn area property: ignoreAreas. 2018-02-22 16:25:38 +01:00
Zukero
f2e4767eb0 Fixed export bug multiplying the data. 2018-02-18 11:28:03 +01:00
Zukero
78ceacb0ce Warnings hunt. 2018-02-13 15:26:13 +01:00
Zukero
1604373e6c Fixed icons alignment. 2018-02-13 14:11:42 +01:00
Zukero
6e2ee13da7 Fixed issue where worldmap.xml was unduely included in the generated
loadresources.xml
2018-02-12 14:18:02 +01:00
Zukero
ea28b7475a Fixed non-critical NPE. 2018-02-12 11:40:36 +01:00
Zukero
ffe6a14cd9 v0.6.10 released. 2018-02-11 13:19:28 +01:00
Zukero
3ab233761f Merge branch 'master' of https://github.com/Zukero/ATCS.git 2018-02-10 14:45:50 +01:00
Zukero
e5bb59b876 Added support for the"alignmentSet" dialogue reward type 2018-02-10 14:43:22 +01:00
Zukero
1fb22ab73f Completed formatting of loadresources.xml files generating by project
export.
2018-02-08 17:19:25 +01:00
Zukero
6b834e0f0e Added sadly convoluted way of pretty-printing loadresources.xml
Still not perfect.
2018-02-05 23:39:07 +01:00
Zukero
9e6e1d936d Fixed a bug in loadresources.xml generation.
Enhanced custom command handling for desktop tools integration.
2018-02-05 18:15:18 +01:00
Zukero
c3144db751 Fixed bug in "Npc->Effect on every hit->Actor conditions applied to the
target". Widget state wasn't updated correctly upon using the radio
buttons.
2018-02-04 11:50:32 +01:00
Zukero
daeb394373 Fixed dialogue reward editor. giveItem reward type now allows negative
amounts.
Added Nut's spritesheets to spritesheet.properties
2018-01-16 23:37:58 +01:00
Zukero
e697f93cf5 Fixed export issue zhere the loadresources.xml file couldn't be created
because its parent folder do not exist.
2017-12-18 19:04:51 +01:00
Zukero
407d50a01e Added Wizard for export project settings. Added export to game source
folder directly. Untested...
2017-12-18 18:58:09 +01:00
Zukero
a475180bb5 Fixed unwanted link following in project deletion that led to deleting
the game source's drawable folder. First steps towards enhancements of
export package generation.
2017-12-17 22:40:46 +01:00
Zukero
259442710b Merge branch 'master' of https://github.com/Zukero/ATCS.git 2017-12-09 15:51:26 +01:00
Zukero
3c63ace6c1 v0.6.9 released. 2017-12-09 15:51:03 +01:00
Zukero
1786860a3b Buf fixes for worldmaps upon contained map deletion. 2017-10-31 15:50:04 +01:00
Zukero
cbc101b3b1 Fixed parsing issue in ActorCondition.
The full round effect's visual effect was not read from the correct
field, causing NPEs.
2017-10-24 11:38:47 +02:00
Zukero
33260137d9 Bug fix in Worldmaps when a composing map is deleted. 2017-10-22 18:21:48 +02:00
Zukero
5a1d8637f9 v0.6.8 released. 2017-10-20 13:36:47 +02:00
Zukero
97119b7101 Fixed copy-pasting bug and made wording more consistent. 2017-10-09 18:52:08 +02:00
Zukero
104029124b Added support for the "revenge strike" feature in NPCs and Items.
Untested yet, and error-prone, because it was made with a LOT of
copy-paste-replacing.
2017-10-09 18:36:41 +02:00
Zukero
2aad37549c Bug fixes and added icon overlay for immunity management. 2017-08-28 13:30:08 +02:00
Zukero
8dc05bd26a Updated UI and model to support Actor Condition Immunity.
Overall, a much better UI to tune Actor Condition-based effects on
Dialogues, Items and NPCs.
2017-08-28 01:05:19 +02:00
Zukero
de0274a5be Updated version number in main class to v0.6.7. 2017-08-25 11:13:02 +02:00
Zukero
3d2dbb9f51 v0.6.7 released! 2017-08-24 20:26:43 +02:00
Zukero
f53302cb18 Font scaling is now also scaling icons, and works in most of the UI. 2017-08-24 18:37:30 +02:00
Zukero
8d6a40eb13 Added font scaling support. It's nowhere near perfect, and some look and
feels don't support it. To set it up, add the -DFONT_SCALE=1.0 (or other
float number) in the JAVA_OPTS environment variables in the startup
scripts.
2017-08-24 18:04:56 +02:00
Zukero
0199bcfb4c Small bug fixes and UI improvements. 2017-08-23 18:17:55 +02:00
Zukero
3fe895a668 Icon for alignmentChange rewards and factionScore requirements 2017-08-22 17:36:46 +02:00
Zukero
025a63af28 Visual effect widget is now a combo box, as the data is an Enum in the
game.
2017-08-21 18:32:11 +02:00
Zukero
528ac7a7e3 Added support for the new "factionScore" requirement type. 2017-08-20 11:19:54 +02:00
Zukero
506afb95ed Simple UI improvements.
Combo box to select skill ID in skill level requirement.
Icons for most requirements types in requirements list in dialogue
editor.
More verbose description of requirement in requirement list.
2017-08-19 11:47:30 +02:00
Zukero
5d802ed0e3 v0.6.6 released. 2017-08-18 22:19:29 +02:00
Zukero
33cbd059ec More bug fixes in Requirement management for Key and Replace areas.
Much better handling of the old school form, and smooth, automatic
transitionning from old school to new school, but only when necessary!
2017-08-18 17:22:06 +02:00
Zukero
6cb0941ca6 Replace areas can now use any type of requirement.
Many bug fix in Key Areas and Replace Areas, especially for the
requirements management.
2017-08-18 16:20:25 +02:00
Zukero
cfb38c33d6 Fixed bug preventing Quests with no quest stages from loading. 2017-08-18 11:51:48 +02:00
Zukero
ec3afaaf36 v0.6.5 released! 2017-08-11 22:32:54 +02:00
Zukero
9f978591ff Fixed XML-refreshing bug in TMX Map editor, and added area name editor
for key areas.
2017-08-11 22:16:08 +02:00
Zukero
7fff58d8f9 Merge branch 'master' of https://github.com/Zukero/ATCS.git 2017-08-11 21:31:57 +02:00
Zukero
6192bd8dce Fixed issue with symlinks creatipon in windows for paths containing
spaces.
2017-08-11 21:31:34 +02:00
Zukero
be43a2a5d4 Fixed display of incorrect default values when the underlying datais
null. Fixed KeyArea initialization.
2017-08-11 18:43:44 +02:00
Zukero
99524bf043 Introducing the help panel for Dialogue Sketches' editor's shortcuts. 2017-08-09 18:04:30 +02:00
Zukero
f2144ab446 Modified marker (*) handled for Dialogue Sketches too.
Typo fixed too, now displays "Dialogue sketches" instead of "Dialogue
sketchs"
2017-08-08 18:43:15 +02:00
Zukero
ada045a13b Fixed JSON import of existing element.
When an imported item was already present in the game sources, it wasnt
associated with the correct file, leading to trouble.
2017-08-08 18:25:01 +02:00
Zukero
4c4f7e5b92 Better management of scrolling in TMX Maps editor's Replacement
simulator.
2017-07-30 17:38:50 +02:00
Zukero
ef521207de v0.6.4 released. 2017-07-28 13:15:42 +02:00
Zukero
3ef0f7e0f1 Update checker now honors the "Use internet" setting. Another setting,
"check for updates" also controls this behavior.
2017-07-28 13:11:30 +02:00
Zukero
74808cdd3a Fixed a bug improperly restoring the state of WriteModeData (Dialogue
sketches) after loading it from disk. This created a risk of data loss
upon subsequent edition of dialogue sketches.
2017-07-27 20:50:10 +02:00
Zukero
1e8d08ee3a Added online version check, to warn of availability of a new version. 2017-07-27 01:07:01 +02:00
Zukero
8d8a1e122e v0.6.3 released! 2017-07-26 23:49:16 +02:00
Zukero
fe62c05b4b Fixed many bugs in the TMX Maps management. Added the
removeQuestProgress dialogue reward type. Initiallized the GDEVisitor
class to help with finding dependencies (through the Beanshell console
only for now).
2017-07-26 15:50:50 +02:00
Zukero
f93d03dbd3 Deleting a TMX Map also removes it from created/altered worldmaps, and
marks these as modified.
2017-07-25 19:16:35 +02:00
Zukero
7eb5c7c208 Fixed NPC editor bug where inflicted actor conditions weren't managed
correctly.
Fixed Worldmap saving and deletion (mostly the * management was crap)
Fixed multi-selection deletion.
2017-07-25 17:01:31 +02:00
Zukero
e04c3ee2fd v0.6.2 released! Redesigned WorldMap editor. Many new UI features. Fixed
Trainer.
2017-05-05 15:07:50 +02:00
Zukero
38a1e90aad Redesigned Worldmap editor. Better UI & support for town labels.
Searchable map list that mirrors the on-map selection.
2017-05-04 14:27:38 +02:00
Zukero
83d459021b Simple bug fix that broke the NPC icon selection window. 2017-04-21 15:55:15 +02:00
Zukero
fb8dcb9fb4 v0.6.1! Rebuilt completely the Quest editor. Each quest stage has its
own backlinks now. Quest log entries and dialogue replies are now
translatable too. Multiple minor UI improvements (notably multiline text
area are now taller, and rewards and requirements appear more clearly in
the dialogue tree view).
2017-04-14 15:52:32 +02:00
Zukero
5e73b59d06 v0.6.0 released! Weblate integration is complete. New icons (created for
weblate integration) now replace to old ugly ones for the notification
area.
2017-04-12 15:57:35 +02:00
Zukero
bca28781bd Level 2 of weblate integration. Rudimentary UI, but working well. 2017-04-11 23:51:50 +02:00
Zukero
899e94c5bc Better layout for clickable links to weblate. 2017-04-10 23:05:52 +02:00
Zukero
0a7cb40dbc Added link to Weblate from translatable strings once the translator mode
is activated in the workspace settings. This required the addition of a
new lib (in source form) for a SipHash implementation.
2017-04-10 18:16:17 +02:00
110 changed files with 7382 additions and 1046 deletions

View File

@@ -3,6 +3,7 @@
<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"/>
@@ -11,6 +12,7 @@
<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="lib" path="lib/AndorsTrainer_v0.1.4.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@@ -1,18 +1,19 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<jardesc>
<jar path="ATContentStudio/ATCS_v0.5.4.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/>
<manifest generateManifest="true" manifestLocation="" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true">
<sealing sealJar="false">
<packagesToSeal/>
<packagesToUnSeal/>
</sealing>
</manifest>
<selectedElements exportClassFiles="true" exportJavaFiles="true" exportOutputFolder="false">
<javaElement handleIdentifier="=ATContentStudio/res"/>
<javaElement handleIdentifier="=ATContentStudio/src"/>
<javaElement handleIdentifier="=ATContentStudio/hacked-libtiled"/>
</selectedElements>
</jardesc>
<?xml version="1.0" encoding="WINDOWS-1252" standalone="no"?>
<jardesc>
<jar path="ATContentStudio/ATCS_v0.6.11.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/>
<manifest generateManifest="true" manifestLocation="" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true">
<sealing sealJar="false">
<packagesToSeal/>
<packagesToUnSeal/>
</sealing>
</manifest>
<selectedElements exportClassFiles="true" exportJavaFiles="true" exportOutputFolder="false">
<javaElement handleIdentifier="=ATContentStudio/hacked-libtiled"/>
<javaElement handleIdentifier="=ATContentStudio/siphash-zackehh\/src\/main\/java"/>
<javaElement handleIdentifier="=ATContentStudio/src"/>
<javaElement handleIdentifier="=ATContentStudio/res"/>
</selectedElements>
</jardesc>

View File

@@ -111,11 +111,14 @@ public class Map implements Iterable<MapLayer>
public MapLayer addLayer(MapLayer layer) {
layer.setMap(this);
layers.add(layer);
if (layer instanceof TileLayer) {
tileLayers.add((TileLayer) layer);
layers.add(layer);
} else if (layer instanceof ObjectGroup) {
layers.insertElementAt(layer, objectGroups.size());
objectGroups.add((ObjectGroup) layer);
} else {
layers.add(layer);
}
return layer;
}

View File

@@ -181,7 +181,12 @@ public class TMXMapWriter
firstgid += tileset.getMaxTileId() + 1;
}
for (MapLayer layer : map) {
for (MapLayer layer : map.getTileLayers()) {
writeMapLayer(layer, w, wp);
}
for (MapLayer layer : map.getObjectGroup()) {
if (map.getTileLayers().contains(layer)) continue;
writeMapLayer(layer, w, wp);
}
firstGidPerTileset = null;

BIN
itemScroll.xcf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
lib/jsoup-1.10.2.jar Normal file

Binary file not shown.

1
packaging/ATCS_latest Normal file
View File

@@ -0,0 +1 @@
v0.6.11

View File

@@ -4,7 +4,7 @@ set "ATCS_DIR=%~dp0"
set "MAX_MEM=512M"
set "CP=%ATCS_DIR%lib\*"
set "JAVA=javaw.exe"
set "JAVA_OPTS="
set "JAVA_OPTS=-DFONT_SCALE=1.0 -Dswing.aatext=true"
set "ENV_FILE=%ATCS_DIR%ATCS.env.bat"
set "MAIN_CLASS=com.gpl.rpg.atcontentstudio.ATContentStudio"

View File

@@ -3,7 +3,7 @@ ATCS_DIR=$(dirname $(readlink -f "$0" || greadlink -f "$0" || stat -f "$0"))
MAX_MEM=512M
CP=$(find ${ATCS_DIR}/lib/ -name '*.jar' | paste -sd: -)
JAVA=java
JAVA_OPTS=
JAVA_OPTS='-DFONT_SCALE=1.0 -Dswing.aatext=true'
ENV_FILE=${ATCS_DIR}/ATCS.env
MAIN_CLASS=com.gpl.rpg.atcontentstudio.ATContentStudio

View File

@@ -4,7 +4,7 @@ set "ATCS_DIR=%~dp0"
set "MAX_MEM=512M"
set "CP=%ATCS_DIR%lib\*"
set "JAVA=javaw.exe"
set "JAVA_OPTS="
set "JAVA_OPTS=-DFONT_SCALE=1.0 -Dswing.aatext=true"
set "ENV_FILE=%ATCS_DIR%ATCS.env.bat"
set "MAIN_CLASS=com.gpl.rpg.atcontentstudio.ATContentStudio"

View File

@@ -1,7 +1,7 @@
!include MUI2.nsh
!define VERSION "0.5.4"
!define TRAINER_VERSION "0.1.3"
!define VERSION "0.6.11"
!define TRAINER_VERSION "0.1.4"
!define JAVA_BIN "javaw"
Name "Andor's Trail Content Studio v${VERSION}"
@@ -92,6 +92,7 @@ Section install
file "rsyntaxtextarea.jar"
file "prefuse.jar"
file "bsh-2.0b4.jar"
file "jsoup-1.10.2.jar"
SetOutPath $INSTDIR
@@ -120,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
View 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.

View 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.

View File

@@ -84,4 +84,5 @@ atcs.spritesheet.effect_bluetentacle.animate=true
atcs.spritesheet.effect_heal2.animate=true
atcs.spritesheet.effect_poison1.animate=true
atcs.spritesheet.effect_tometik1.animate=true
atcs.spritesheet.effect_tometik2.animate=true
atcs.spritesheet.effect_tometik2.animate=true
atcs.spritesheet.monsters_guynmart.category=monster

19
siphash-zackehh/.gitignore vendored Normal file
View 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

View File

@@ -0,0 +1,3 @@
language: java
script:
- mvn clean test jacoco:report coveralls:report

21
siphash-zackehh/LICENSE Normal file
View 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
View File

@@ -0,0 +1,77 @@
# SipHash
[![Build Status](https://travis-ci.org/zackehh/siphash-java.svg?branch=master)](https://travis-ci.org/zackehh/siphash-java) [![Coverage Status](https://coveralls.io/repos/zackehh/siphash-java/badge.svg?branch=master&service=github)](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
View 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>

View File

@@ -0,0 +1,111 @@
package com.zackehh.siphash;
import static com.zackehh.siphash.SipHashConstants.DEFAULT_C;
import static com.zackehh.siphash.SipHashConstants.DEFAULT_D;
import static com.zackehh.siphash.SipHashConstants.INITIAL_V0;
import static com.zackehh.siphash.SipHashConstants.INITIAL_V1;
import static com.zackehh.siphash.SipHashConstants.INITIAL_V2;
import static com.zackehh.siphash.SipHashConstants.INITIAL_V3;
/**
* Main entry point for SipHash, providing a basic hash
* 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();
}
}

View File

@@ -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 }

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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");
}
}

View File

@@ -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();
}
}

View File

@@ -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");
}
}

View File

@@ -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
}
}

View File

@@ -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");
}
}

View File

@@ -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));
}
}

View File

@@ -1,17 +1,36 @@
package com.gpl.rpg.atcontentstudio;
import java.awt.Color;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.plaf.FontUIResource;
import prefuse.data.expression.parser.ExpressionParser;
@@ -24,9 +43,15 @@ 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.4";
public static final String APP_VERSION = "v0.6.11";
public static final String CHECK_UPDATE_URL = "https://andorstrail.com/static/ATCS_latest";
public static final String DOWNLOAD_URL = "https://andorstrail.com/viewtopic.php?f=6&t=4806";
public static final String FONT_SCALE_ENV_VAR_NAME = "FONT_SCALE";
public static boolean STARTED = false;
public static float SCALING=1.0f;
public static StudioFrame frame = null;
//Need to keep a strong reference to it, to avoid garbage collection that'll reset these loggers.
@@ -36,6 +61,17 @@ public class ATContentStudio {
* @param args
*/
public static void main(String[] args) {
String fontScaling = System.getProperty(FONT_SCALE_ENV_VAR_NAME);
Float fontScale = null;
if (fontScaling != null) {
try {
fontScale = Float.parseFloat(fontScaling);
SCALING=fontScale;
} catch (NumberFormatException e) {
System.err.println("Failed to parse font scaling parameter. Using default.");
e.printStackTrace();
}
}
ConfigCache.init();
@@ -52,6 +88,9 @@ public class ATContentStudio {
} catch (UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
scaleUIFont();
//Need to keep a strong reference to it, to avoid garbage collection that'll reset this setting.
Logger l = Logger.getLogger(ExpressionParser.class.getName());
@@ -74,6 +113,11 @@ public class ATContentStudio {
WorkerDialog.showTaskMessage("Loading your workspace...", null, new Runnable(){
public void run() {
Workspace.setActive(workspaceRoot);
if (Workspace.activeWorkspace.settings.useInternet.getCurrentValue() && Workspace.activeWorkspace.settings.checkUpdates.getCurrentValue()) {
new Thread() {
public void run() {checkUpdate();}
}.start();
}
frame = new StudioFrame(APP_NAME+" "+APP_VERSION);
frame.setVisible(true);
frame.setDefaultCloseOperation(StudioFrame.DO_NOTHING_ON_CLOSE);
@@ -95,4 +139,89 @@ public class ATContentStudio {
});
}
private static void checkUpdate() {
BufferedReader in = null;
try {
URL url = new URL(CHECK_UPDATE_URL);
in = new BufferedReader(new InputStreamReader(url.openStream()));
String inputLine, lastLine = null;
while ((inputLine = in.readLine()) != null) {lastLine = inputLine;}
if (lastLine != null && !lastLine.equals(APP_VERSION)) {
// for copying style
JLabel label = new JLabel();
Font font = label.getFont();
Color color = label.getBackground();
// create some css from the label's font
StringBuffer style = new StringBuffer("font-family:" + font.getFamily() + ";");
style.append("font-weight:" + (font.isBold() ? "bold" : "normal") + ";");
style.append("font-size:" + font.getSize() + "pt;");
style.append("background-color: rgb("+color.getRed()+","+color.getGreen()+","+color.getBlue()+");");
JEditorPane ep = new JEditorPane("text/html", "<html><body style=\"" + style + "\">"
+ "You are not running the latest ATCS version.<br/>"
+ "You can get the latest version ("+lastLine+") by clicking the link below.<br/>"
+ "<a href=\""+DOWNLOAD_URL+"\">"+DOWNLOAD_URL+"</a><br/>"
+ "<br/>"
+ "</body></html>");
ep.setEditable(false);
ep.setBorder(null);
ep.addHyperlinkListener(new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
try {
if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) {
Desktop.getDesktop().browse(e.getURL().toURI());
}
} catch (IOException e1) {
e1.printStackTrace();
} catch (URISyntaxException e1) {
e1.printStackTrace();
}
}
});
JOptionPane.showMessageDialog(null, ep, "Update available", JOptionPane.INFORMATION_MESSAGE);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (in != null) in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void scaleUIFont() {
if (SCALING != 1.0f) {
System.out.println("Scaling fonts to "+SCALING);
UIDefaults defaults = UIManager.getLookAndFeelDefaults();
Map<Object, Object> newDefaults = new HashMap<Object, Object>();
for (Enumeration<Object> e = defaults.keys(); e.hasMoreElements();) {
Object key = e.nextElement();
Object value = defaults.get(key);
if (value instanceof Font) {
Font font = (Font) value;
int newSize = (int)(font.getSize() * SCALING);
if (value instanceof FontUIResource) {
newDefaults.put(key, new FontUIResource(font.getName(), font.getStyle(), newSize));
} else {
newDefaults.put(key, new Font(font.getName(), font.getStyle(), newSize));
}
}
}
for (Object key : newDefaults.keySet()) {
defaults.put(key, newDefaults.get(key));
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 607 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 566 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1013 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1023 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 880 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 881 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 481 B

View File

@@ -1,28 +1,47 @@
package com.gpl.rpg.atcontentstudio.model;
import java.awt.Image;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.swing.tree.TreeNode;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.json.simple.JSONArray;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import com.gpl.rpg.atcontentstudio.ATContentStudio;
import com.gpl.rpg.atcontentstudio.Notification;
@@ -39,6 +58,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;
@@ -221,6 +241,8 @@ public class Project implements ProjectTreeNode, Serializable {
public void refreshTransients(Workspace w) {
this.parent = w;
projectElementListeners = new HashMap<Class<? extends GameDataElement>, List<ProjectElementListener>>();
try {
knownSpritesheetsProperties = new Properties();
knownSpritesheetsProperties.load(Project.class.getResourceAsStream("/spritesheets.properties"));
@@ -253,7 +275,6 @@ public class Project implements ProjectTreeNode, Serializable {
linkAll();
projectElementListeners = new HashMap<Class<? extends GameDataElement>, List<ProjectElementListener>>();
}
public void linkAll() {
@@ -669,7 +690,7 @@ public class Project implements ProjectTreeNode, Serializable {
public Spritesheet getSpritesheet(int index) {
if (index < createdContent.gameSprites.spritesheets.size()) {
return createdContent.gameSprites.spritesheets.get(index);
} else if (index < getQuestCount()){
} else if (index < getSpritesheetCount()){
return getSpritesheet(baseContent.gameSprites.spritesheets.get(index - createdContent.gameSprites.spritesheets.size()).id);
}
return null;
@@ -758,6 +779,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);
}
@@ -1032,85 +1062,149 @@ public class Project implements ProjectTreeNode, Serializable {
}
}
public void generateExportPackage(final File target) {
public void exportProjectAsZipPackage(final File target) {
WorkerDialog.showTaskMessage("Exporting project "+name+"...", ATContentStudio.frame, true, new Runnable() {
@Override
public void run() {
Notification.addInfo("Exporting project \""+name+"\" as "+target.getAbsolutePath());
File tmpDir = new File(baseFolder, "tmp");
FileUtils.deleteDir(tmpDir);
tmpDir.mkdir();
File tmpJsonDataDir = new File(tmpDir, GameDataSet.DEFAULT_REL_PATH_IN_SOURCE);
tmpJsonDataDir.mkdirs();
for (File createdJsonFile : createdContent.gameData.baseFolder.listFiles()) {
FileUtils.copyFile(createdJsonFile, new File(tmpJsonDataDir, createdJsonFile.getName()));
}
writeAltered(alteredContent.gameData.actorConditions, baseContent.gameData.actorConditions, ActorCondition.class, tmpJsonDataDir);
writeAltered(alteredContent.gameData.dialogues, baseContent.gameData.dialogues, Dialogue.class, tmpJsonDataDir);
writeAltered(alteredContent.gameData.droplists, baseContent.gameData.droplists, Droplist.class, tmpJsonDataDir);
writeAltered(alteredContent.gameData.itemCategories, baseContent.gameData.itemCategories, ItemCategory.class, tmpJsonDataDir);
writeAltered(alteredContent.gameData.items, baseContent.gameData.items, Item.class, tmpJsonDataDir);
writeAltered(alteredContent.gameData.npcs, baseContent.gameData.npcs, NPC.class, tmpJsonDataDir);
writeAltered(alteredContent.gameData.quests, baseContent.gameData.quests, Quest.class, tmpJsonDataDir);
File tmpMapDir = new File(tmpDir, TMXMapSet.DEFAULT_REL_PATH_IN_SOURCE);
tmpMapDir.mkdirs();
for (File createdMapFile : createdContent.gameMaps.mapFolder.listFiles()) {
FileUtils.copyFile(createdMapFile, new File(tmpMapDir, createdMapFile.getName()));
}
for (File alteredMapFile : alteredContent.gameMaps.mapFolder.listFiles()) {
FileUtils.copyFile(alteredMapFile, new File(tmpMapDir, alteredMapFile.getName()));
}
if (!createdContent.worldmap.isEmpty() || !alteredContent.worldmap.isEmpty()) {
try {
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
doc.setXmlVersion("1.0");
Element root = doc.createElement("worldmap");
doc.appendChild(root);
for (int i = 0; i < getWorldmapSegmentCount(); i++) {
root.appendChild(getWorldmapSegment(i).toXmlElement(doc));
}
Worldmap.saveDocToFile(doc, new File(tmpMapDir, "worldmap.xml"));
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
File tmpDir = exportProjectToTmpDir();
FileUtils.writeToZip(tmpDir, target);
FileUtils.deleteDir(tmpDir);
Notification.addSuccess("Project \""+name+"\" exported as "+target.getAbsolutePath());
}
});
}
@SuppressWarnings("rawtypes")
public void writeAltered(GameDataCategory<? extends JSONElement> altered, GameDataCategory<? extends JSONElement> source, Class<? extends JSONElement> gdeClass, File targetFolder) {
Set<String> alteredFileNames = new LinkedHashSet<String>();
Map<String, List<Map>> toWrite = new LinkedHashMap<String, List<Map>>();
for (JSONElement gde : altered) {
alteredFileNames.add(gde.jsonFile.getName());
public void exportProjectOverGameSource(final File target) {
WorkerDialog.showTaskMessage("Exporting project "+name+"...", ATContentStudio.frame, true, new Runnable() {
@Override
public void run() {
Notification.addInfo("Exporting project \""+name+"\" into "+target.getAbsolutePath());
File tmpDir = exportProjectToTmpDir();
FileUtils.copyOver(tmpDir, target);
FileUtils.deleteDir(tmpDir);
Notification.addSuccess("Project \""+name+"\" exported into "+target.getAbsolutePath());
}
});
}
public File exportProjectToTmpDir() {
File tmpDir = new File(baseFolder, "tmp");
FileUtils.deleteDir(tmpDir);
tmpDir.mkdir();
File tmpJsonDataDir = new File(tmpDir, GameDataSet.DEFAULT_REL_PATH_IN_SOURCE);
tmpJsonDataDir.mkdirs();
// for (File createdJsonFile : createdContent.gameData.baseFolder.listFiles()) {
// FileUtils.copyFile(createdJsonFile, new File(tmpJsonDataDir, createdJsonFile.getName()));
// }
Map<Class<? extends GameDataElement>, List<String>> writtenFilesPerDataType = new LinkedHashMap<Class<? extends GameDataElement>, List<String>>();
List<String> writtenFiles;
writtenFiles = writeDataDeltaForDataType(createdContent.gameData.actorConditions, alteredContent.gameData.actorConditions, baseContent.gameData.actorConditions, ActorCondition.class, tmpJsonDataDir);
writtenFilesPerDataType.put(ActorCondition.class, writtenFiles);
writtenFiles = writeDataDeltaForDataType(createdContent.gameData.dialogues, alteredContent.gameData.dialogues, baseContent.gameData.dialogues, Dialogue.class, tmpJsonDataDir);
writtenFilesPerDataType.put(Dialogue.class, writtenFiles);
writtenFiles = writeDataDeltaForDataType(createdContent.gameData.droplists, alteredContent.gameData.droplists, baseContent.gameData.droplists, Droplist.class, tmpJsonDataDir);
writtenFilesPerDataType.put(Droplist.class, writtenFiles);
writtenFiles = writeDataDeltaForDataType(createdContent.gameData.itemCategories, alteredContent.gameData.itemCategories, baseContent.gameData.itemCategories, ItemCategory.class, tmpJsonDataDir);
writtenFilesPerDataType.put(ItemCategory.class, writtenFiles);
writtenFiles = writeDataDeltaForDataType(createdContent.gameData.items, alteredContent.gameData.items, baseContent.gameData.items, Item.class, tmpJsonDataDir);
writtenFilesPerDataType.put(Item.class, writtenFiles);
writtenFiles = writeDataDeltaForDataType(createdContent.gameData.npcs, alteredContent.gameData.npcs, baseContent.gameData.npcs, NPC.class, tmpJsonDataDir);
writtenFilesPerDataType.put(NPC.class, writtenFiles);
writtenFiles = writeDataDeltaForDataType(createdContent.gameData.quests, alteredContent.gameData.quests, baseContent.gameData.quests, Quest.class, tmpJsonDataDir);
writtenFilesPerDataType.put(Quest.class, writtenFiles);
File tmpMapDir = new File(tmpDir, TMXMapSet.DEFAULT_REL_PATH_IN_SOURCE);
tmpMapDir.mkdirs();
writtenFiles = new LinkedList<String>();
for (File createdMapFile : createdContent.gameMaps.mapFolder.listFiles()) {
if (createdMapFile.getName().equalsIgnoreCase("worldmap.xml")) continue;
FileUtils.copyFile(createdMapFile, new File(tmpMapDir, createdMapFile.getName()));
writtenFiles.add(createdMapFile.getName());
}
for (String fName : alteredFileNames) {
for (File alteredMapFile : alteredContent.gameMaps.mapFolder.listFiles()) {
if (alteredMapFile.getName().equalsIgnoreCase("worldmap.xml")) continue;
FileUtils.copyFile(alteredMapFile, new File(tmpMapDir, alteredMapFile.getName()));
writtenFiles.add(alteredMapFile.getName());
}
writtenFilesPerDataType.put(TMXMap.class, writtenFiles);
if (sourceSetToUse == ResourceSet.gameData) {
writeResourceListXml(writtenFilesPerDataType, GameSource.DEFAULT_REL_PATH_FOR_GAME_RESOURCE, baseContent.baseFolder, tmpDir);
} else if (sourceSetToUse == ResourceSet.debugData) {
writeResourceListXml(writtenFilesPerDataType, GameSource.DEFAULT_REL_PATH_FOR_DEBUG_RESOURCE, baseContent.baseFolder, tmpDir);
}
if (!createdContent.worldmap.isEmpty() || !alteredContent.worldmap.isEmpty()) {
try {
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
doc.setXmlVersion("1.0");
Element root = doc.createElement("worldmap");
doc.appendChild(root);
for (int i = 0; i < getWorldmapSegmentCount(); i++) {
root.appendChild(getWorldmapSegment(i).toXmlElement(doc));
}
Worldmap.saveDocToFile(doc, new File(tmpMapDir, "worldmap.xml"));
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return tmpDir;
}
@SuppressWarnings("rawtypes")
public List<String> writeDataDeltaForDataType(GameDataCategory<? extends JSONElement> created, GameDataCategory<? extends JSONElement> altered, GameDataCategory<? extends JSONElement> source, Class<? extends JSONElement> gdeClass, File targetFolder) {
List<String> filenamesToWrite = new LinkedList<String>();
Map<String, List<Map>> dataToWritePerFilename = new LinkedHashMap<String, List<Map>>();
for (JSONElement gde : altered) {
if (!filenamesToWrite.contains(gde.jsonFile.getName())) {
filenamesToWrite.add(gde.jsonFile.getName());
}
}
for (JSONElement gde : created) {
if (!filenamesToWrite.contains(gde.jsonFile.getName())) {
filenamesToWrite.add(gde.jsonFile.getName());
}
}
for (String fName : filenamesToWrite) {
for (JSONElement gde : source) {
if (gde.jsonFile.getName().equals(fName)) {
if (toWrite.get(fName) == null) {
toWrite.put(fName, new ArrayList<Map>());
if (dataToWritePerFilename.get(fName) == null) {
dataToWritePerFilename.put(fName, new ArrayList<Map>());
}
toWrite.get(fName).add(getGameDataElement(gdeClass, gde.id).toJson());
//Automatically fetches altered element over source element.
dataToWritePerFilename.get(fName).add(getGameDataElement(gdeClass, gde.id).toJson());
}
}
for (JSONElement gde : created) {
if (gde.jsonFile.getName().equals(fName)) {
if (dataToWritePerFilename.get(fName) == null) {
dataToWritePerFilename.put(fName, new ArrayList<Map>());
}
//Add the created elements.
dataToWritePerFilename.get(fName).add(getGameDataElement(gdeClass, gde.id).toJson());
}
}
}
for (String fName : toWrite.keySet()) {
for (String fName : dataToWritePerFilename.keySet()) {
File jsonFile = new File(targetFolder, fName);
StringWriter writer = new JsonPrettyWriter();
try {
JSONArray.writeJSONString(toWrite.get(fName), writer);
JSONArray.writeJSONString(dataToWritePerFilename.get(fName), writer);
} catch (IOException e) {
//Impossible with a StringWriter
}
@@ -1125,8 +1219,138 @@ public class Project implements ProjectTreeNode, Serializable {
e.printStackTrace();
}
}
return filenamesToWrite;
}
private void writeResourceListXml(Map<Class<? extends GameDataElement>, List<String>> writtenFilesPerDataType, String xmlFileRelPath, File baseFolder, File tmpDir) {
File xmlFile = new File(baseFolder, xmlFileRelPath);
File outputFile = new File(tmpDir, xmlFileRelPath);
Map<String, Class<? extends GameDataElement>> classNamesByArrayNames = new HashMap<String, Class<? extends GameDataElement>>();
classNamesByArrayNames.put("loadresource_itemcategories", ItemCategory.class);
classNamesByArrayNames.put("loadresource_actorconditions", ActorCondition.class);
classNamesByArrayNames.put("loadresource_items", Item.class);
classNamesByArrayNames.put("loadresource_droplists", Droplist.class);
classNamesByArrayNames.put("loadresource_quests", Quest.class);
classNamesByArrayNames.put("loadresource_conversationlists", Dialogue.class);
classNamesByArrayNames.put("loadresource_monsters", NPC.class);
classNamesByArrayNames.put("loadresource_maps", TMXMap.class);
String jsonResPrefix = "@raw/";
String tmxResPrefix = "@xml/";
String jsonFileSuffix = ".json";
String tmxFileSuffix = ".tmx";
if (!xmlFile.exists()) return;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
Document doc;
try {
factory.setIgnoringElementContentWhitespace(true);
factory.setExpandEntityReferences(false);
DocumentBuilder builder = factory.newDocumentBuilder();
InputSource insrc = new InputSource(new FileInputStream(xmlFile));
insrc.setEncoding("UTF-8");
doc = builder.parse(insrc);
Element arrayNode;
String name, resPrefix, fileSuffix, resName, resToFile, fileToRes;
Class<? extends GameDataElement> clazz;
List<String> writtenFiles;
Element root = (Element) doc.getElementsByTagName("resources").item(0);
if (root != null) {
NodeList arraysList = root.getElementsByTagName("array");
if (arraysList != null) {
for (int i = 0; i < arraysList.getLength(); i++) {
arrayNode = (Element) arraysList.item(i);
name = arrayNode.getAttribute("name");
clazz = classNamesByArrayNames.get(name);
if (clazz == null) continue;
writtenFiles = writtenFilesPerDataType.get(clazz);
if (writtenFiles == null) continue;
if (clazz == TMXMap.class) {
resPrefix = tmxResPrefix;
fileSuffix = tmxFileSuffix;
} else {
resPrefix = jsonResPrefix;
fileSuffix = jsonFileSuffix;
}
NodeList arrayItems = arrayNode.getElementsByTagName("item");
if (arrayItems != null) {
for (int j = 0; j < arrayItems.getLength(); j++) {
resName = ((Element)arrayItems.item(j)).getTextContent();
if (resName == null) continue;
resToFile = resName.replaceFirst("\\A"+resPrefix, "")+fileSuffix;
writtenFiles.remove(resToFile);
}
}
if (!writtenFiles.isEmpty()) {
Comment com = doc.createComment("Added by ATCS "+ATContentStudio.APP_VERSION+" for project "+getProject().name);
arrayNode.appendChild(com);
Collections.sort(writtenFiles);
for (String missingRes : writtenFiles) {
Element item = doc.createElement("item");
fileToRes = resPrefix+missingRes.replaceFirst(fileSuffix+"\\z", "");
item.setTextContent(fileToRes);
arrayNode.appendChild(item);
}
}
}
}
}
Transformer transformer = TransformerFactory.newInstance().newTransformer();
if (!outputFile.getParentFile().exists()) {
outputFile.getParentFile().mkdirs();
}
StringWriter temp = new StringWriter();
Result output = new StreamResult(temp);
Source input = new DOMSource(doc);
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.transform(input, output);
String tempString = temp.toString();
doc = builder.parse(new ByteArrayInputStream(tempString.getBytes("UTF-8")));
input = new DOMSource(doc);
transformer = TransformerFactory.newInstance().newTransformer(new StreamSource(new StringReader(
"<?xml version=\"1.0\"?>\r\n" +
"<xsl:stylesheet version=\"1.0\"\r\n" +
" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\r\n" +
" <xsl:strip-space elements=\"*\" />\r\n" +
" <xsl:output method=\"xml\" indent=\"yes\" />\r\n" +
"\r\n" +
" <xsl:template match=\"node() | @*\" name=\"identity\">\r\n" +
" <xsl:copy>\r\n" +
" <xsl:apply-templates select=\"node() | @*\" />\r\n" +
" </xsl:copy>\r\n" +
" </xsl:template>\r\n" +
"\r\n" +
" <xsl:template match=\"array\">\r\n" +
" <xsl:call-template name=\"identity\"/>\r\n" +
" <xsl:text>&#xA;&#xA;&#x20;&#x20;&#x20;&#x20;</xsl:text>\r\n" +
" </xsl:template>\r\n" +
"</xsl:stylesheet>")));
output = new StreamResult(new FileOutputStream(outputFile));
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
transformer.transform(input, output);
} catch (SAXException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
}
}
@Override
public boolean needsSaving() {

View File

@@ -4,6 +4,7 @@ import java.awt.Image;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
@@ -332,7 +333,9 @@ public class Workspace implements ProjectTreeNode, Serializable {
private static boolean delete(File f) {
boolean b = true;
if (f.isDirectory()) {
if (Files.isSymbolicLink(f.toPath())) {
b &= f.delete();
} else if (f.isDirectory()) {
for (File c : f.listFiles())
b &= delete(c);
}

View File

@@ -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,14 @@ 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 static Boolean DEFAULT_CHECK_UPDATE = true;
public Setting<Boolean> checkUpdates = new PrimitiveSetting<Boolean>("checkUpdates", DEFAULT_CHECK_UPDATE);
public List<Setting<? extends Object>> settings = new ArrayList<Setting<? extends Object>>();
public WorkspaceSettings(Workspace parent) {
@@ -50,6 +55,9 @@ public class WorkspaceSettings {
settings.add(useSystemDefaultImageViewer);
settings.add(useSystemDefaultImageEditor);
settings.add(imageEditorCommand);
settings.add(translatorLanguage);
settings.add(useInternet);
settings.add(checkUpdates);
file = new File(parent.baseFolder, FILENAME);
if (file.exists()) {
load(file);
@@ -150,7 +158,7 @@ public class WorkspaceSettings {
value = defaultValue;
}
public abstract void readFromJson(Map json);
public abstract void readFromJson(@SuppressWarnings("rawtypes") Map json);
@SuppressWarnings({ "rawtypes", "unchecked" })
public void saveToJson(Map json) {
@@ -174,6 +182,19 @@ public class WorkspaceSettings {
}
public class NullDefaultPrimitiveSetting<X extends Object> extends PrimitiveSetting<X> {
public NullDefaultPrimitiveSetting(String id) {
super(id, null);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@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) {
@@ -181,6 +202,7 @@ public class WorkspaceSettings {
this.value = this.defaultValue = defaultValue;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public void readFromJson(Map json) {
value = new ArrayList<X>();

View File

@@ -21,7 +21,9 @@ public class ActorCondition extends JSONElement {
private static final long serialVersionUID = -3969824899972048507L;
public static final Integer CLEAR_AC_MAGNITUDE = -99;
public static final Integer MAGNITUDE_CLEAR = -99;
public static final Integer DURATION_FOREVER = 999;;
public static final Integer DURATION_NONE = 0;
// Available from init state
//public String id; inherited.
@@ -43,9 +45,16 @@ public class ActorCondition extends JSONElement {
blood
}
public static enum VisualEffectID {
redSplash
,blueSwirl
,greenSplash
,miss
}
public static class RoundEffect implements Cloneable {
// Available from parsed state
public String visual_effect = null;
public VisualEffectID visual_effect = null;
public Integer hp_boost_min = null;
public Integer hp_boost_max = null;
public Integer ap_boost_min = null;
@@ -180,7 +189,13 @@ public class ActorCondition extends JSONElement {
this.round_effect.ap_boost_max = JSONElement.getInteger((Number) (((Map)roundEffect.get("increaseCurrentAP")).get("max")));
this.round_effect.ap_boost_min = JSONElement.getInteger((Number) (((Map)roundEffect.get("increaseCurrentAP")).get("min")));
}
this.round_effect.visual_effect = (String) roundEffect.get("visualEffectID");
String vfx = (String) roundEffect.get("visualEffectID");
this.round_effect.visual_effect = null;
if (vfx != null) {
try {
this.round_effect.visual_effect = VisualEffectID.valueOf(vfx);
} catch(IllegalArgumentException e) {}
}
}
Map fullRoundEffect = (Map) aCondJson.get("fullRoundEffect");
if (fullRoundEffect != null) {
@@ -193,7 +208,13 @@ public class ActorCondition extends JSONElement {
this.full_round_effect.ap_boost_max = JSONElement.getInteger((Number) (((Map)fullRoundEffect.get("increaseCurrentAP")).get("max")));
this.full_round_effect.ap_boost_min = JSONElement.getInteger((Number) (((Map)fullRoundEffect.get("increaseCurrentAP")).get("min")));
}
this.full_round_effect.visual_effect = (String) fullRoundEffect.get("visualEffectID");
String vfx = (String) fullRoundEffect.get("visualEffectID");
this.full_round_effect.visual_effect = null;
if (vfx != null) {
try {
this.full_round_effect.visual_effect = VisualEffectID.valueOf(vfx);
} catch(IllegalArgumentException e) {}
}
}
this.state = State.parsed;
@@ -214,6 +235,9 @@ public class ActorCondition extends JSONElement {
}
if (this.icon_id != null) {
String spritesheetId = this.icon_id.split(":")[0];
if (getProject().getSpritesheet(spritesheetId) == null) {
System.out.println(this.id);
}
getProject().getSpritesheet(spritesheetId).addBacklink(this);
}
@@ -275,7 +299,7 @@ public class ActorCondition extends JSONElement {
if (this.stacking != null && this.stacking == 1) jsonAC.put("isStacking", this.stacking);
if (this.round_effect != null) {
Map jsonRound = new LinkedHashMap();
if (this.round_effect.visual_effect != null) jsonRound.put("visualEffectID", this.round_effect.visual_effect);
if (this.round_effect.visual_effect != null) jsonRound.put("visualEffectID", this.round_effect.visual_effect.toString());
if (this.round_effect.hp_boost_min != null || this.round_effect.hp_boost_max != null) {
Map jsonHP = new LinkedHashMap();
if (this.round_effect.hp_boost_min != null) jsonHP.put("min", this.round_effect.hp_boost_min);
@@ -296,7 +320,7 @@ public class ActorCondition extends JSONElement {
}
if (this.full_round_effect != null) {
Map jsonFullRound = new LinkedHashMap();
if (this.full_round_effect.visual_effect != null) jsonFullRound.put("visualEffectID", this.full_round_effect.visual_effect);
if (this.full_round_effect.visual_effect != null) jsonFullRound.put("visualEffectID", this.full_round_effect.visual_effect.toString());
if (this.full_round_effect.hp_boost_min != null || this.full_round_effect.hp_boost_max != null) {
Map jsonHP = new LinkedHashMap();
if (this.full_round_effect.hp_boost_min != null) jsonHP.put("min", this.full_round_effect.hp_boost_min);

View File

@@ -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;
@@ -55,10 +54,13 @@ public class Dialogue extends JSONElement {
public enum RewardType {
questProgress,
removeQuestProgress,
dropList,
skillIncrease,
actorCondition,
actorConditionImmunity,
alignmentChange,
alignmentSet,
giveItem,
createTimer,
spawnAll,
@@ -250,10 +252,12 @@ public class Dialogue extends JSONElement {
reward.map = reward.map_name != null ? proj.getMap(reward.map_name) : null;
break;
case actorCondition:
case actorConditionImmunity:
reward.reward_obj = proj.getActorCondition(reward.reward_obj_id);
break;
case alignmentChange:
//Nothing to do (yet ?).
case alignmentSet:
//Nothing to do (yet ?).
break;
case createTimer:
//Nothing to do.
@@ -265,7 +269,14 @@ public class Dialogue extends JSONElement {
reward.reward_obj = proj.getItem(reward.reward_obj_id);
break;
case questProgress:
case removeQuestProgress:
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 +373,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 +385,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);
}
}
}
}
}

View File

@@ -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;

View File

@@ -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 {
@@ -35,6 +34,7 @@ public class Item extends JSONElement {
public String category_id = null;
public String description = null;
public HitEffect hit_effect = null;
public HitReceivedEffect hit_received_effect = null;
public KillEffect kill_effect = null;
public EquipEffect equip_effect = null;
@@ -59,6 +59,14 @@ public class Item extends JSONElement {
public List<TimedConditionEffect> conditions_target = null;
}
public static class HitReceivedEffect extends HitEffect {
//Available from parsed state
public Integer hp_boost_min_target = null;
public Integer hp_boost_max_target = null;
public Integer ap_boost_min_target = null;
public Integer ap_boost_max_target = null;
}
public static class EquipEffect {
//Available from parsed state
public Integer damage_boost_min = null;
@@ -247,6 +255,53 @@ public class Item extends JSONElement {
}
}
Map hitReceivedEffect = (Map) itemJson.get("hitReceivedEffect");
if (hitReceivedEffect != null) {
this.hit_received_effect = new HitReceivedEffect();
if (hitReceivedEffect.get("increaseCurrentHP") != null) {
this.hit_received_effect.hp_boost_min = JSONElement.getInteger((Number) (((Map)hitReceivedEffect.get("increaseCurrentHP")).get("min")));
this.hit_received_effect.hp_boost_max = JSONElement.getInteger((Number) (((Map)hitReceivedEffect.get("increaseCurrentHP")).get("max")));
}
if (hitReceivedEffect.get("increaseCurrentAP") != null) {
this.hit_received_effect.ap_boost_min = JSONElement.getInteger((Number) (((Map)hitReceivedEffect.get("increaseCurrentAP")).get("min")));
this.hit_received_effect.ap_boost_max = JSONElement.getInteger((Number) (((Map)hitReceivedEffect.get("increaseCurrentAP")).get("max")));
}
if (hitReceivedEffect.get("increaseAttackerCurrentHP") != null) {
this.hit_received_effect.hp_boost_min_target = JSONElement.getInteger((Number) (((Map)hitReceivedEffect.get("increaseAttackerCurrentHP")).get("min")));
this.hit_received_effect.hp_boost_max_target = JSONElement.getInteger((Number) (((Map)hitReceivedEffect.get("increaseAttackerCurrentHP")).get("max")));
}
if (hitReceivedEffect.get("increaseAttackerCurrentAP") != null) {
this.hit_received_effect.ap_boost_min_target = JSONElement.getInteger((Number) (((Map)hitReceivedEffect.get("increaseAttackerCurrentAP")).get("min")));
this.hit_received_effect.ap_boost_max_target = JSONElement.getInteger((Number) (((Map)hitReceivedEffect.get("increaseAttackerCurrentAP")).get("max")));
}
List conditionsSourceJson = (List) hitReceivedEffect.get("conditionsSource");
if (conditionsSourceJson != null && !conditionsSourceJson.isEmpty()) {
this.hit_received_effect.conditions_source = new ArrayList<Item.TimedConditionEffect>();
for (Object conditionJsonObj : conditionsSourceJson) {
Map conditionJson = (Map)conditionJsonObj;
TimedConditionEffect condition = new TimedConditionEffect();
condition.condition_id = (String) conditionJson.get("condition");
condition.magnitude = JSONElement.getInteger((Number) conditionJson.get("magnitude"));
condition.duration = JSONElement.getInteger((Number) conditionJson.get("duration"));
if (conditionJson.get("chance") != null) condition.chance = JSONElement.parseChance(conditionJson.get("chance").toString());
this.hit_received_effect.conditions_source.add(condition);
}
}
List conditionsTargetJson = (List) hitReceivedEffect.get("conditionsTarget");
if (conditionsTargetJson != null && !conditionsTargetJson.isEmpty()) {
this.hit_received_effect.conditions_target = new ArrayList<Item.TimedConditionEffect>();
for (Object conditionJsonObj : conditionsTargetJson) {
Map conditionJson = (Map)conditionJsonObj;
TimedConditionEffect condition = new TimedConditionEffect();
condition.condition_id = (String) conditionJson.get("condition");
condition.magnitude = JSONElement.getInteger((Number) conditionJson.get("magnitude"));
condition.duration = JSONElement.getInteger((Number) conditionJson.get("duration"));
if (conditionJson.get("chance") != null) condition.chance = JSONElement.parseChance(conditionJson.get("chance").toString());
this.hit_received_effect.conditions_target.add(condition);
}
}
}
Map killEffect = (Map) itemJson.get("killEffect");
if (killEffect == null) {
killEffect = (Map) itemJson.get("useEffect");
@@ -322,6 +377,18 @@ public class Item extends JSONElement {
if (ce.condition != null) ce.condition.addBacklink(this);
}
}
if (this.hit_received_effect != null && this.hit_received_effect.conditions_source != null) {
for (TimedConditionEffect ce : this.hit_received_effect.conditions_source) {
if (ce.condition_id != null) ce.condition = proj.getActorCondition(ce.condition_id);
if (ce.condition != null) ce.condition.addBacklink(this);
}
}
if (this.hit_received_effect != null && this.hit_received_effect.conditions_target != null) {
for (TimedConditionEffect ce : this.hit_received_effect.conditions_target) {
if (ce.condition_id != null) ce.condition = proj.getActorCondition(ce.condition_id);
if (ce.condition != null) ce.condition.addBacklink(this);
}
}
if (this.kill_effect != null && this.kill_effect.conditions_source != null) {
for (TimedConditionEffect ce : this.kill_effect.conditions_source) {
if (ce.condition_id != null) ce.condition = proj.getActorCondition(ce.condition_id);
@@ -423,6 +490,47 @@ public class Item extends JSONElement {
}
}
}
if (this.hit_received_effect != null) {
clone.hit_received_effect = new HitReceivedEffect();
clone.hit_received_effect.ap_boost_max = this.hit_received_effect.ap_boost_max;
clone.hit_received_effect.ap_boost_min = this.hit_received_effect.ap_boost_min;
clone.hit_received_effect.hp_boost_max = this.hit_received_effect.hp_boost_max;
clone.hit_received_effect.hp_boost_min = this.hit_received_effect.hp_boost_min;
clone.hit_received_effect.ap_boost_max_target = this.hit_received_effect.ap_boost_max_target;
clone.hit_received_effect.ap_boost_min_target = this.hit_received_effect.ap_boost_min_target;
clone.hit_received_effect.hp_boost_max_target = this.hit_received_effect.hp_boost_max_target;
clone.hit_received_effect.hp_boost_min_target = this.hit_received_effect.hp_boost_min_target;
if (this.hit_received_effect.conditions_source != null) {
clone.hit_received_effect.conditions_source = new ArrayList<Item.TimedConditionEffect>();
for (TimedConditionEffect c : this.hit_received_effect.conditions_source) {
TimedConditionEffect cclone = new TimedConditionEffect();
cclone.magnitude = c.magnitude;
cclone.condition_id = c.condition_id;
cclone.condition = c.condition;
cclone.chance = c.chance;
cclone.duration = c.duration;
if (cclone.condition != null) {
cclone.condition.addBacklink(clone);
}
clone.hit_received_effect.conditions_source.add(cclone);
}
}
if (this.hit_received_effect.conditions_target != null) {
clone.hit_received_effect.conditions_target = new ArrayList<Item.TimedConditionEffect>();
for (TimedConditionEffect c : this.hit_received_effect.conditions_target) {
TimedConditionEffect cclone = new TimedConditionEffect();
cclone.magnitude = c.magnitude;
cclone.condition_id = c.condition_id;
cclone.condition = c.condition;
cclone.chance = c.chance;
cclone.duration = c.duration;
if (cclone.condition != null) {
cclone.condition.addBacklink(clone);
}
clone.hit_received_effect.conditions_target.add(cclone);
}
}
}
if (this.kill_effect != null) {
clone.kill_effect = new KillEffect();
clone.kill_effect.ap_boost_max = this.kill_effect.ap_boost_max;
@@ -601,6 +709,74 @@ public class Item extends JSONElement {
}
}
}
if (this.hit_received_effect != null) {
Map hitReceivedEffectJson = new LinkedHashMap();
itemJson.put("hitReceivedEffect", hitReceivedEffectJson);
if (this.hit_received_effect.hp_boost_min != null || this.hit_received_effect.hp_boost_max != null) {
Map hpJson = new LinkedHashMap();
hitReceivedEffectJson.put("increaseCurrentHP", hpJson);
if (this.hit_received_effect.hp_boost_min != null) hpJson.put("min", this.hit_received_effect.hp_boost_min);
else hpJson.put("min", 0);
if (this.hit_received_effect.hp_boost_max != null) hpJson.put("max", this.hit_received_effect.hp_boost_max);
else hpJson.put("max", 0);
}
if (this.hit_received_effect.ap_boost_min != null || this.hit_received_effect.ap_boost_max != null) {
Map apJson = new LinkedHashMap();
hitReceivedEffectJson.put("increaseCurrentAP", apJson);
if (this.hit_received_effect.ap_boost_min != null) apJson.put("min", this.hit_received_effect.ap_boost_min);
else apJson.put("min", 0);
if (this.hit_received_effect.ap_boost_max != null) apJson.put("max", this.hit_received_effect.ap_boost_max);
else apJson.put("max", 0);
}
if (this.hit_received_effect.hp_boost_min_target != null || this.hit_received_effect.hp_boost_max_target != null) {
Map hpJson = new LinkedHashMap();
hitReceivedEffectJson.put("increaseAttackerCurrentHP", hpJson);
if (this.hit_received_effect.hp_boost_min_target != null) hpJson.put("min", this.hit_received_effect.hp_boost_min_target);
else hpJson.put("min", 0);
if (this.hit_received_effect.hp_boost_max_target != null) hpJson.put("max", this.hit_received_effect.hp_boost_max_target);
else hpJson.put("max", 0);
}
if (this.hit_received_effect.ap_boost_min_target != null || this.hit_received_effect.ap_boost_max_target != null) {
Map apJson = new LinkedHashMap();
hitReceivedEffectJson.put("increaseAttackerCurrentAP", apJson);
if (this.hit_received_effect.ap_boost_min_target != null) apJson.put("min", this.hit_received_effect.ap_boost_min_target);
else apJson.put("min", 0);
if (this.hit_received_effect.ap_boost_max_target != null) apJson.put("max", this.hit_received_effect.ap_boost_max_target);
else apJson.put("max", 0);
}
if (this.hit_received_effect.conditions_source != null) {
List conditionsSourceJson = new ArrayList();
hitReceivedEffectJson.put("conditionsSource", conditionsSourceJson);
for (TimedConditionEffect condition : this.hit_received_effect.conditions_source) {
Map conditionJson = new LinkedHashMap();
conditionsSourceJson.add(conditionJson);
if (condition.condition != null) {
conditionJson.put("condition", condition.condition.id);
} else if (condition.condition_id != null) {
conditionJson.put("condition", condition.condition_id);
}
if (condition.magnitude != null) conditionJson.put("magnitude", condition.magnitude);
if (condition.duration != null) conditionJson.put("duration", condition.duration);
if (condition.chance != null) conditionJson.put("chance", JSONElement.printJsonChance(condition.chance));
}
}
if (this.hit_received_effect.conditions_target != null) {
List conditionsTargetJson = new ArrayList();
hitReceivedEffectJson.put("conditionsTarget", conditionsTargetJson);
for (TimedConditionEffect condition : this.hit_received_effect.conditions_target) {
Map conditionJson = new LinkedHashMap();
conditionsTargetJson.add(conditionJson);
if (condition.condition != null) {
conditionJson.put("condition", condition.condition.id);
} else if (condition.condition_id != null) {
conditionJson.put("condition", condition.condition_id);
}
if (condition.magnitude != null) conditionJson.put("magnitude", condition.magnitude);
if (condition.duration != null) conditionJson.put("duration", condition.duration);
if (condition.chance != null) conditionJson.put("chance", JSONElement.printJsonChance(condition.chance));
}
}
}
if (this.kill_effect != null) {
Map killEffectJson = new LinkedHashMap();
if (this.category != null && this.category.action_type != null && this.category.action_type == ItemCategory.ActionType.equip) {

View File

@@ -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 {

View File

@@ -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 {
@@ -48,6 +47,8 @@ public class NPC extends JSONElement {
public Integer block_chance = null;
public Integer damage_resistance = null;
public HitEffect hit_effect = null;
public HitReceivedEffect hit_received_effect = null;
public DeathEffect death_effect = null;
//Available from linked state
public Dialogue dialogue = null;
@@ -71,17 +72,29 @@ public class NPC extends JSONElement {
protectSpawn,
wholeMap
}
public static class HitEffect {
public static class DeathEffect {
//Available from parsed state
public Integer hp_boost_min = null;
public Integer hp_boost_max = null;
public Integer ap_boost_min = null;
public Integer ap_boost_max = null;
public List<TimedConditionEffect> conditions_source = null;
}
public static class HitEffect extends DeathEffect {
//Available from parsed state
public List<TimedConditionEffect> conditions_target = null;
}
public static class HitReceivedEffect extends HitEffect {
//Available from parsed state
public Integer hp_boost_min_target = null;
public Integer hp_boost_max_target = null;
public Integer ap_boost_min_target = null;
public Integer ap_boost_max_target = null;
}
public static class TimedConditionEffect {
//Available from parsed state
public Integer magnitude = null;
@@ -225,6 +238,79 @@ public class NPC extends JSONElement {
}
}
Map hitReceivedEffect = (Map) npcJson.get("hitReceivedEffect");
if (hitReceivedEffect != null) {
this.hit_received_effect = new HitReceivedEffect();
if (hitReceivedEffect.get("increaseCurrentHP") != null) {
this.hit_received_effect.hp_boost_max = JSONElement.getInteger((Number) (((Map)hitReceivedEffect.get("increaseCurrentHP")).get("max")));
this.hit_received_effect.hp_boost_min = JSONElement.getInteger((Number) (((Map)hitReceivedEffect.get("increaseCurrentHP")).get("min")));
}
if (hitReceivedEffect.get("increaseCurrentAP") != null) {
this.hit_received_effect.ap_boost_max = JSONElement.getInteger((Number) (((Map)hitReceivedEffect.get("increaseCurrentAP")).get("max")));
this.hit_received_effect.ap_boost_min = JSONElement.getInteger((Number) (((Map)hitReceivedEffect.get("increaseCurrentAP")).get("min")));
}
if (hitReceivedEffect.get("increaseAttackerCurrentHP") != null) {
this.hit_received_effect.hp_boost_max_target = JSONElement.getInteger((Number) (((Map)hitReceivedEffect.get("increaseAttackerCurrentHP")).get("max")));
this.hit_received_effect.hp_boost_min_target = JSONElement.getInteger((Number) (((Map)hitReceivedEffect.get("increaseAttackerCurrentHP")).get("min")));
}
if (hitReceivedEffect.get("increaseAttackerCurrentAP") != null) {
this.hit_received_effect.ap_boost_max_target = JSONElement.getInteger((Number) (((Map)hitReceivedEffect.get("increaseAttackerCurrentAP")).get("max")));
this.hit_received_effect.ap_boost_min_target = JSONElement.getInteger((Number) (((Map)hitReceivedEffect.get("increaseAttackerCurrentAP")).get("min")));
}
List conditionsSourceJson = (List) hitReceivedEffect.get("conditionsSource");
if (conditionsSourceJson != null && !conditionsSourceJson.isEmpty()) {
this.hit_received_effect.conditions_source = new ArrayList<NPC.TimedConditionEffect>();
for (Object conditionJsonObj : conditionsSourceJson) {
Map conditionJson = (Map)conditionJsonObj;
TimedConditionEffect condition = new TimedConditionEffect();
condition.condition_id = (String) conditionJson.get("condition");
condition.magnitude = JSONElement.getInteger((Number) conditionJson.get("magnitude"));
condition.duration = JSONElement.getInteger((Number) conditionJson.get("duration"));
if (conditionJson.get("chance") != null) condition.chance = JSONElement.parseChance(conditionJson.get("chance").toString());
this.hit_received_effect.conditions_source.add(condition);
}
}
List conditionsTargetJson = (List) hitReceivedEffect.get("conditionsTarget");
if (conditionsTargetJson != null && !conditionsTargetJson.isEmpty()) {
this.hit_received_effect.conditions_target = new ArrayList<NPC.TimedConditionEffect>();
for (Object conditionJsonObj : conditionsTargetJson) {
Map conditionJson = (Map)conditionJsonObj;
TimedConditionEffect condition = new TimedConditionEffect();
condition.condition_id = (String) conditionJson.get("condition");
condition.magnitude = JSONElement.getInteger((Number) conditionJson.get("magnitude"));
condition.duration = JSONElement.getInteger((Number) conditionJson.get("duration"));
if (conditionJson.get("chance") != null) condition.chance = JSONElement.parseChance(conditionJson.get("chance").toString());
this.hit_received_effect.conditions_target.add(condition);
}
}
}
Map deathEffect = (Map) npcJson.get("deathEffect");
if (deathEffect != null) {
this.death_effect = new HitEffect();
if (deathEffect.get("increaseCurrentHP") != null) {
this.death_effect.hp_boost_max = JSONElement.getInteger((Number) (((Map)deathEffect.get("increaseCurrentHP")).get("max")));
this.death_effect.hp_boost_min = JSONElement.getInteger((Number) (((Map)deathEffect.get("increaseCurrentHP")).get("min")));
}
if (deathEffect.get("increaseCurrentAP") != null) {
this.death_effect.ap_boost_max = JSONElement.getInteger((Number) (((Map)deathEffect.get("increaseCurrentAP")).get("max")));
this.death_effect.ap_boost_min = JSONElement.getInteger((Number) (((Map)deathEffect.get("increaseCurrentAP")).get("min")));
}
List conditionsSourceJson = (List) deathEffect.get("conditionsSource");
if (conditionsSourceJson != null && !conditionsSourceJson.isEmpty()) {
this.death_effect.conditions_source = new ArrayList<NPC.TimedConditionEffect>();
for (Object conditionJsonObj : conditionsSourceJson) {
Map conditionJson = (Map)conditionJsonObj;
TimedConditionEffect condition = new TimedConditionEffect();
condition.condition_id = (String) conditionJson.get("condition");
condition.magnitude = JSONElement.getInteger((Number) conditionJson.get("magnitude"));
condition.duration = JSONElement.getInteger((Number) conditionJson.get("duration"));
if (conditionJson.get("chance") != null) condition.chance = JSONElement.parseChance(conditionJson.get("chance").toString());
this.death_effect.conditions_source.add(condition);
}
}
}
}
@Override
@@ -268,6 +354,24 @@ public class NPC extends JSONElement {
if (ce.condition != null) ce.condition.addBacklink(this);
}
}
if (this.hit_received_effect != null && this.hit_received_effect.conditions_source != null) {
for (TimedConditionEffect ce : this.hit_received_effect.conditions_source) {
if (ce.condition_id != null) ce.condition = proj.getActorCondition(ce.condition_id);
if (ce.condition != null) ce.condition.addBacklink(this);
}
}
if (this.hit_received_effect != null && this.hit_received_effect.conditions_target != null) {
for (TimedConditionEffect ce : this.hit_received_effect.conditions_target) {
if (ce.condition_id != null) ce.condition = proj.getActorCondition(ce.condition_id);
if (ce.condition != null) ce.condition.addBacklink(this);
}
}
if (this.death_effect != null && this.death_effect.conditions_source != null) {
for (TimedConditionEffect ce : this.death_effect.conditions_source) {
if (ce.condition_id != null) ce.condition = proj.getActorCondition(ce.condition_id);
if (ce.condition != null) ce.condition.addBacklink(this);
}
}
this.state = State.linked;
}
@@ -344,6 +448,69 @@ public class NPC extends JSONElement {
}
}
}
if (this.hit_received_effect != null) {
clone.hit_received_effect = new HitReceivedEffect();
clone.hit_received_effect.ap_boost_max = this.hit_received_effect.ap_boost_max;
clone.hit_received_effect.ap_boost_min = this.hit_received_effect.ap_boost_min;
clone.hit_received_effect.hp_boost_max = this.hit_received_effect.hp_boost_max;
clone.hit_received_effect.hp_boost_min = this.hit_received_effect.hp_boost_min;
clone.hit_received_effect.ap_boost_max_target = this.hit_received_effect.ap_boost_max_target;
clone.hit_received_effect.ap_boost_min_target = this.hit_received_effect.ap_boost_min_target;
clone.hit_received_effect.hp_boost_max_target = this.hit_received_effect.hp_boost_max_target;
clone.hit_received_effect.hp_boost_min_target = this.hit_received_effect.hp_boost_min_target;
if (this.hit_received_effect.conditions_source != null) {
clone.hit_received_effect.conditions_source = new ArrayList<TimedConditionEffect>();
for (TimedConditionEffect c : this.hit_received_effect.conditions_source) {
TimedConditionEffect cclone = new TimedConditionEffect();
cclone.magnitude = c.magnitude;
cclone.condition_id = c.condition_id;
cclone.condition = c.condition;
cclone.chance = c.chance;
cclone.duration = c.duration;
if (cclone.condition != null) {
cclone.condition.addBacklink(clone);
}
clone.hit_received_effect.conditions_source.add(cclone);
}
}
if (this.hit_received_effect.conditions_target != null) {
clone.hit_received_effect.conditions_target = new ArrayList<TimedConditionEffect>();
for (TimedConditionEffect c : this.hit_received_effect.conditions_target) {
TimedConditionEffect cclone = new TimedConditionEffect();
cclone.magnitude = c.magnitude;
cclone.condition_id = c.condition_id;
cclone.condition = c.condition;
cclone.chance = c.chance;
cclone.duration = c.duration;
if (cclone.condition != null) {
cclone.condition.addBacklink(clone);
}
clone.hit_received_effect.conditions_target.add(cclone);
}
}
}
if (this.death_effect != null) {
clone.death_effect = new DeathEffect();
clone.death_effect.ap_boost_max = this.death_effect.ap_boost_max;
clone.death_effect.ap_boost_min = this.death_effect.ap_boost_min;
clone.death_effect.hp_boost_max = this.death_effect.hp_boost_max;
clone.death_effect.hp_boost_min = this.death_effect.hp_boost_min;
if (this.death_effect.conditions_source != null) {
clone.death_effect.conditions_source = new ArrayList<TimedConditionEffect>();
for (TimedConditionEffect c : this.death_effect.conditions_source) {
TimedConditionEffect cclone = new TimedConditionEffect();
cclone.magnitude = c.magnitude;
cclone.condition_id = c.condition_id;
cclone.condition = c.condition;
cclone.chance = c.chance;
cclone.duration = c.duration;
if (cclone.condition != null) {
cclone.condition.addBacklink(clone);
}
clone.death_effect.conditions_source.add(cclone);
}
}
}
clone.max_ap = this.max_ap;
clone.max_hp = this.max_hp;
clone.monster_class = this.monster_class;
@@ -479,6 +646,110 @@ public class NPC extends JSONElement {
}
}
}
if (this.hit_received_effect != null) {
Map hitReceivedEffectJson = new LinkedHashMap();
npcJson.put("hitReceivedEffect", hitReceivedEffectJson);
if (this.hit_received_effect.hp_boost_min != null || this.hit_received_effect.hp_boost_max != null) {
Map hpJson = new LinkedHashMap();
hitReceivedEffectJson.put("increaseCurrentHP", hpJson);
if (this.hit_received_effect.hp_boost_min != null) hpJson.put("min", this.hit_received_effect.hp_boost_min);
else hpJson.put("min", 0);
if (this.hit_received_effect.hp_boost_max != null) hpJson.put("max", this.hit_received_effect.hp_boost_max);
else hpJson.put("max", 0);
}
if (this.hit_received_effect.ap_boost_min != null || this.hit_received_effect.ap_boost_max != null) {
Map apJson = new LinkedHashMap();
hitReceivedEffectJson.put("increaseCurrentAP", apJson);
if (this.hit_received_effect.ap_boost_min != null) apJson.put("min", this.hit_received_effect.ap_boost_min);
else apJson.put("min", 0);
if (this.hit_received_effect.ap_boost_max != null) apJson.put("max", this.hit_received_effect.ap_boost_max);
else apJson.put("max", 0);
}
if (this.hit_received_effect.hp_boost_min_target != null || this.hit_received_effect.hp_boost_max_target != null) {
Map hpJson = new LinkedHashMap();
hitReceivedEffectJson.put("increaseAttackerCurrentHP", hpJson);
if (this.hit_received_effect.hp_boost_min_target != null) hpJson.put("min", this.hit_received_effect.hp_boost_min_target);
else hpJson.put("min", 0);
if (this.hit_received_effect.hp_boost_max_target != null) hpJson.put("max", this.hit_received_effect.hp_boost_max_target);
else hpJson.put("max", 0);
}
if (this.hit_received_effect.ap_boost_min_target != null || this.hit_received_effect.ap_boost_max_target != null) {
Map apJson = new LinkedHashMap();
hitReceivedEffectJson.put("increaseAttackerCurrentAP", apJson);
if (this.hit_received_effect.ap_boost_min_target != null) apJson.put("min", this.hit_received_effect.ap_boost_min_target);
else apJson.put("min", 0);
if (this.hit_received_effect.ap_boost_max_target != null) apJson.put("max", this.hit_received_effect.ap_boost_max_target);
else apJson.put("max", 0);
}
if (this.hit_received_effect.conditions_source != null) {
List conditionsSourceJson = new ArrayList();
hitReceivedEffectJson.put("conditionsSource", conditionsSourceJson);
for (TimedConditionEffect condition : this.hit_received_effect.conditions_source) {
Map conditionJson = new LinkedHashMap();
conditionsSourceJson.add(conditionJson);
if (condition.condition != null) {
conditionJson.put("condition", condition.condition.id);
} else if (condition.condition_id != null) {
conditionJson.put("condition", condition.condition_id);
}
if (condition.magnitude != null) conditionJson.put("magnitude", condition.magnitude);
if (condition.duration != null) conditionJson.put("duration", condition.duration);
if (condition.chance != null) conditionJson.put("chance", JSONElement.printJsonChance(condition.chance));
}
}
if (this.hit_received_effect.conditions_target != null) {
List conditionsTargetJson = new ArrayList();
hitReceivedEffectJson.put("conditionsTarget", conditionsTargetJson);
for (TimedConditionEffect condition : this.hit_received_effect.conditions_target) {
Map conditionJson = new LinkedHashMap();
conditionsTargetJson.add(conditionJson);
if (condition.condition != null) {
conditionJson.put("condition", condition.condition.id);
} else if (condition.condition_id != null) {
conditionJson.put("condition", condition.condition_id);
}
if (condition.magnitude != null) conditionJson.put("magnitude", condition.magnitude);
if (condition.duration != null) conditionJson.put("duration", condition.duration);
if (condition.chance != null) conditionJson.put("chance", JSONElement.printJsonChance(condition.chance));
}
}
}
if (this.death_effect != null) {
Map deathEffectJson = new LinkedHashMap();
npcJson.put("deathEffect", deathEffectJson);
if (this.death_effect.hp_boost_min != null || this.death_effect.hp_boost_max != null) {
Map hpJson = new LinkedHashMap();
deathEffectJson.put("increaseCurrentHP", hpJson);
if (this.death_effect.hp_boost_min != null) hpJson.put("min", this.death_effect.hp_boost_min);
else hpJson.put("min", 0);
if (this.death_effect.hp_boost_max != null) hpJson.put("max", this.death_effect.hp_boost_max);
else hpJson.put("max", 0);
}
if (this.death_effect.ap_boost_min != null || this.death_effect.ap_boost_max != null) {
Map apJson = new LinkedHashMap();
deathEffectJson.put("increaseCurrentAP", apJson);
if (this.death_effect.ap_boost_min != null) apJson.put("min", this.death_effect.ap_boost_min);
else apJson.put("min", 0);
if (this.death_effect.ap_boost_max != null) apJson.put("max", this.death_effect.ap_boost_max);
else apJson.put("max", 0);
}
if (this.death_effect.conditions_source != null) {
List conditionsSourceJson = new ArrayList();
deathEffectJson.put("conditionsSource", conditionsSourceJson);
for (TimedConditionEffect condition : this.death_effect.conditions_source) {
Map conditionJson = new LinkedHashMap();
conditionsSourceJson.add(conditionJson);
if (condition.condition != null) {
conditionJson.put("condition", condition.condition.id);
} else if (condition.condition_id != null) {
conditionJson.put("condition", condition.condition_id);
}
if (condition.magnitude != null) conditionJson.put("magnitude", condition.magnitude);
if (condition.duration != null) conditionJson.put("duration", condition.duration);
if (condition.chance != null) conditionJson.put("chance", JSONElement.printJsonChance(condition.chance));
}
}
}
return npcJson;
}

View File

@@ -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,22 +30,6 @@ 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 (needsSaving() ? "*" : "")+name+" ("+id+")";
@@ -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;
}
@@ -114,19 +99,16 @@ public class Quest extends JSONElement {
public void parse(Map questJson) {
this.visible_in_log = JSONElement.getInteger((Number) questJson.get("showInLog"));
List questStagesJson = (List) questJson.get("stages");
this.stages = new ArrayList<QuestStage>();
if (questStagesJson != null && !questStagesJson.isEmpty()) {
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;
}
}

View 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();
}
}

View File

@@ -53,12 +53,61 @@ public class Requirement extends JSONElement {
usedItem,
spentGold,
consumedBonemeals,
hasActorCondition
hasActorCondition,
factionScore
}
public enum SkillID {
weaponChance
,weaponDmg
,barter
,dodge
,barkSkin
,moreCriticals
,betterCriticals
,speed // Raises max ap
,coinfinder
,moreExp
,cleave // +10ap on kill
,eater // +1hp per kill
,fortitude // +N hp per levelup
,evasion // increase successful flee chance & reduce chance of monster attack
,regeneration // +N hp per round
,lowerExploss
,magicfinder
,resistanceMental // lowers chance to get negative active conditions by monsters (Mental like Dazed)
,resistancePhysical // lowers chance to get negative active conditions by monsters (Physical Capacity like Minor fatigue)
,resistanceBlood // lowers chance to get negative active conditions by monsters (Blood Disorder like Weak Poison)
,shadowBless
,crit1 // lowers atk ability
,crit2 // lowers def ability ,rejuvenation // Reduces magnitudes of conditions
,rejuvenation // Reduces magnitudes of conditions
,taunt // Causes AP loss of attackers that miss
,concussion // AC loss for monsters with (AC-BC)>N
,weaponProficiencyDagger
,weaponProficiency1hsword
,weaponProficiency2hsword
,weaponProficiencyAxe
,weaponProficiencyBlunt
,weaponProficiencyUnarmed
,armorProficiencyShield
,armorProficiencyUnarmored
,armorProficiencyLight
,armorProficiencyHeavy
,fightstyleDualWield
,fightstyle2hand
,fightstyleWeaponShield
,specializationDualWield
,specialization2hand
,specializationWeaponShield
}
@Override
public String getDesc() {
return ((negated != null && negated) ? "NOT " : "")+required_obj_id+(required_value == null ? "" : ":"+required_value.toString());
return ((negated != null && negated) ? "NOT " : "")
+(type == null ? "" : type.toString()+":")
+(required_obj_id == null ? "" : required_obj_id+":")
+(required_value == null ? "" : required_value.toString());
}
@Override
@@ -112,11 +161,18 @@ 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:
case spentGold:
case timerElapsed:
case factionScore:
break;
}
if (this.required_obj != null) this.required_obj.addBacklink((GameDataElement) this.parent);
@@ -150,6 +206,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() {

View File

@@ -19,20 +19,20 @@ public class KeyArea extends MapObject {
String requireType = obj.getProperties().getProperty("requireType");
String requireId = obj.getProperties().getProperty("requireId");
String requireValue = obj.getProperties().getProperty("requireValue");
if (requireId == null) {
oldSchoolRequirement = false;
if (requireType == null) {
String[] fields = obj.getName().split(":");
if (fields.length == 2) {
requireType = Requirement.RequirementType.questProgress.toString();
requireValue = fields[1];
requireId = fields[0];
oldSchoolRequirement = true;
} else if (fields.length == 3) {
requireValue = fields[2];
requireType = fields[0];
requireId = fields[1];
oldSchoolRequirement = true;
}
oldSchoolRequirement = true;
} else {
oldSchoolRequirement = false;
}
requirement = new Requirement();
if (requireType != null) requirement.type = Requirement.RequirementType.valueOf(requireType);
@@ -76,7 +76,7 @@ public class KeyArea extends MapObject {
}
if (requirement != null) {
if (oldSchoolRequirement && Requirement.RequirementType.questProgress.equals(requirement.type) && (requirement.negated == null || !requirement.negated)) {
tmxObject.setName(requirement.required_obj_id+":"+Integer.toString(requirement.required_value));
tmxObject.setName(requirement.required_obj_id+":"+((requirement.required_value == null) ? "" : Integer.toString(requirement.required_value)));
} else {
if (requirement.type != null) {
tmxObject.getProperties().setProperty("requireType", requirement.type.toString());
@@ -89,13 +89,16 @@ public class KeyArea extends MapObject {
if (requirement.required_value != null) {
tmxObject.getProperties().setProperty("requireValue", requirement.required_value.toString());
}
if (requirement.negated != null) {
tmxObject.getProperties().setProperty("requireNegation", Boolean.toString(requirement.negated));
}
}
}
}
public void updateNameFromRequirementChange() {
if (oldSchoolRequirement && Requirement.RequirementType.questProgress.equals(requirement.type) && (requirement.negated == null || !requirement.negated)) {
name = requirement.required_obj_id+":"+Integer.toString(requirement.required_value);
name = requirement.required_obj_id+":"+((requirement.required_value == null) ? "" : Integer.toString(requirement.required_value));
} else if (oldSchoolRequirement) {
int i = 0;
String futureName = requirement.type.toString() + "#" + Integer.toString(i);

View File

@@ -12,39 +12,36 @@ import com.gpl.rpg.atcontentstudio.ui.DefaultIcons;
public class ReplaceArea extends MapObject {
public Requirement requirement = null;
public boolean oldSchoolRequirement = true;
public boolean oldSchoolRequirement = false;
public List<ReplaceArea.Replacement> replacements = null;
public ReplaceArea(tiled.core.MapObject obj) {
// String requireType = obj.getProperties().getProperty("requireType");
String requireType = obj.getProperties().getProperty("requireType");
String requireId = obj.getProperties().getProperty("requireId");
String requireValue = obj.getProperties().getProperty("requireValue");
if (requireId == null) {
if (requireType == null) {
String[] fields = obj.getName().split(":");
if (fields.length == 2) {
// requireType = Requirement.RequirementType.questProgress.toString();
requireType = Requirement.RequirementType.questProgress.toString();
requireValue = fields[1];
requireId = fields[0];
oldSchoolRequirement = true;
} /*else if (fields.length == 3) {
requireValue = fields[2];
requireType = fields[0];
requireId = fields[1];
}*/
oldSchoolRequirement = true;
} else {
oldSchoolRequirement = false;
}
requirement = new Requirement();
//Replace areas only support questProgress requirements ATM
//requirement.type = Requirement.RequirementType.valueOf(requireType);
requirement.type = Requirement.RequirementType.questProgress;
if (requireType != null) requirement.type = Requirement.RequirementType.valueOf(requireType);
requirement.required_obj_id = requireId;
if (requireValue != null) requirement.required_value = Integer.parseInt(requireValue);
requirement.state = GameDataElement.State.parsed;
for (Object s : obj.getProperties().keySet()) {
if (!TMXMap.isPaintedLayerName(s.toString())) continue;
if (replacements == null) replacements = new ArrayList<ReplaceArea.Replacement>();
replacements.add(new Replacement(s.toString(), obj.getProperties().getProperty(s.toString())));
}
@@ -94,7 +91,7 @@ public class ReplaceArea extends MapObject {
}
if (requirement != null) {
if (oldSchoolRequirement && Requirement.RequirementType.questProgress.equals(requirement.type) && (requirement.negated == null || !requirement.negated)) {
tmxObject.setName(requirement.required_obj_id+":"+Integer.toString(requirement.required_value));
tmxObject.setName(requirement.required_obj_id+":"+((requirement.required_value == null) ? "" : Integer.toString(requirement.required_value)));
} else {
tmxObject.getProperties().setProperty("requireType", requirement.type.toString());
if (requirement.required_obj != null) {
@@ -105,6 +102,9 @@ public class ReplaceArea extends MapObject {
if (requirement.required_value != null) {
tmxObject.getProperties().setProperty("requireValue", requirement.required_value.toString());
}
if (requirement.negated != null) {
tmxObject.getProperties().setProperty("requireNegation", Boolean.toString(requirement.negated));
}
}
}
}
@@ -112,7 +112,7 @@ public class ReplaceArea extends MapObject {
//Don't use yet !
public void updateNameFromRequirementChange() {
if (oldSchoolRequirement && Requirement.RequirementType.questProgress.equals(requirement.type) && (requirement.negated == null || !requirement.negated)) {
name = requirement.required_obj_id+":"+Integer.toString(requirement.required_value);
name = requirement.required_obj_id+":"+((requirement.required_value == null) ? "" : Integer.toString(requirement.required_value));
} else if (oldSchoolRequirement) {
int i = 0;
String futureName = requirement.type.toString() + "#" + Integer.toString(i);

View File

@@ -13,6 +13,7 @@ public class SpawnArea extends MapObject {
public int quantity = 1;
public int spawnchance = 10;
public boolean active = true;
public boolean ignoreAreas = false;
public String spawngroup_id;
public List<NPC> spawnGroup = new ArrayList<NPC>();
@@ -26,6 +27,9 @@ public class SpawnArea extends MapObject {
if (obj.getProperties().getProperty("active") != null) {
this.active = Boolean.parseBoolean(obj.getProperties().getProperty("active"));
}
if (obj.getProperties().getProperty("ignoreAreas") != null) {
this.ignoreAreas = Boolean.parseBoolean(obj.getProperties().getProperty("ignoreAreas"));
}
if (obj.getProperties().getProperty("spawngroup") != null) {
this.spawngroup_id = obj.getProperties().getProperty("spawngroup");
} else if (obj.getName() != null ){
@@ -84,6 +88,9 @@ public class SpawnArea extends MapObject {
if (!this.active) {
tmxObject.getProperties().setProperty("active", Boolean.toString(active));
}
if (this.ignoreAreas) {
tmxObject.getProperties().setProperty("ignoreAreas", Boolean.toString(ignoreAreas));
}
}
}

View File

@@ -38,6 +38,7 @@ public class TMXMap extends GameDataElement {
public static final String GROUND_LAYER_NAME = "Ground";
public static final String OBJECTS_LAYER_NAME = "Objects";
public static final String ABOVE_LAYER_NAME = "Above";
public static final String TOP_LAYER_NAME = "Top";
public static final String WALKABLE_LAYER_NAME = "Walkable";
public enum ColorFilter {
@@ -273,11 +274,12 @@ public class TMXMap extends GameDataElement {
public void save() {
if (writable) {
String xml = toXml();
try {
//TODO: check in fileutils, to test the workspace's filesystem once at startup, and figure out how many of these can occur, instead of hard-coded '2'
dismissNextChangeNotif += 2;
FileWriter w = new FileWriter(tmxFile);
w.write(toXml());
w.write(xml);
w.close();
this.state = State.saved;
changedOnDisk = false;
@@ -397,6 +399,7 @@ public class TMXMap extends GameDataElement {
return GROUND_LAYER_NAME.equalsIgnoreCase(name) ||
OBJECTS_LAYER_NAME.equalsIgnoreCase(name) ||
ABOVE_LAYER_NAME.equalsIgnoreCase(name) ||
TOP_LAYER_NAME.equalsIgnoreCase(name) ||
WALKABLE_LAYER_NAME.equalsIgnoreCase(name);
}

View File

@@ -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;
@@ -16,6 +15,7 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.swing.tree.TreeNode;
@@ -26,7 +26,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;
@@ -94,29 +93,31 @@ public class TMXMapSet implements ProjectTreeNode {
});
if (source.type == GameSource.Type.created | source.type == GameSource.Type.altered) {
final Path folderPath = Paths.get(mapFolder.getAbsolutePath());
Thread watcher = new Thread("Map folder watcher for "+source.type) {
Thread watcher = new Thread("Map folder watcher for "+getProject().name+"/"+source.type) {
public void run() {
WatchService watchService;
while(getProject().open) {
try {
watchService = FileSystems.getDefault().newWatchService();
WatchKey watchKey = folderPath.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
/*WatchKey watchKey = */folderPath.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
WatchKey wk;
validService: while(getProject().open) {
wk = watchService.take();
for (WatchEvent<?> event : wk.pollEvents()) {
Path changed = (Path) event.context();
String name = changed.getFileName().toString();
String id = name.substring(0, name.length() - 4);
TMXMap map = getMap(id);
if (map != null) {
map.mapChangedOnDisk();
wk = watchService.poll(10, TimeUnit.SECONDS);
if (wk != null) {
for (WatchEvent<?> event : wk.pollEvents()) {
Path changed = (Path) event.context();
String name = changed.getFileName().toString();
String id = name.substring(0, name.length() - 4);
TMXMap map = getMap(id);
if (map != null) {
map.mapChangedOnDisk();
}
}
if(!wk.reset()) {
watchService.close();
break validService;
}
}
if(!wk.reset()) {
watchService.close();
break validService;
}
}
} catch (IOException e) {

View File

@@ -35,6 +35,7 @@ import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import com.gpl.rpg.atcontentstudio.model.GameDataElement;
import com.gpl.rpg.atcontentstudio.model.GameSource;
import com.gpl.rpg.atcontentstudio.model.GameSource.Type;
import com.gpl.rpg.atcontentstudio.model.Project;
@@ -222,6 +223,7 @@ public class Worldmap extends ArrayList<WorldmapSegment> implements ProjectTreeN
for (WorldmapSegment segment : this) {
root.appendChild(segment.toXmlElement(doc));
segment.state = GameDataElement.State.saved;
}
saveDocToFile(doc, worldmapFile);

View File

@@ -23,7 +23,9 @@ import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
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.SaveEvent;
import com.gpl.rpg.atcontentstudio.model.gamedata.GameDataSet;
import com.gpl.rpg.atcontentstudio.ui.DefaultIcons;
@@ -32,6 +34,8 @@ public class WorldmapSegment extends GameDataElement {
private static final long serialVersionUID = 2658610076889592723L;
public static final String TEMP_LABEL_KEY = "ATCS_INTERNAL_TEMPORARY_KEY_FOR_LABEL";
public int segmentX;
public int segmentY;
public Map<String, Point> mapLocations = new LinkedHashMap<String, Point>();
@@ -102,8 +106,37 @@ public class WorldmapSegment extends GameDataElement {
@Override
public void elementChanged(GameDataElement oldOne, GameDataElement newOne) {
boolean modified = false;
if (newOne == null && writable) {
//A referenced map may have been deleted.
if (mapLocations.containsKey(oldOne.id)) {
mapLocations.remove(oldOne.id);
modified = true;
}
List<String> deprecatedLabels = new ArrayList<String>();
for (String label : labelledMaps.keySet()) {
if (labelledMaps.get(label).contains(oldOne.id)) {
labelledMaps.get(label).remove(oldOne.id);
modified = true;
if (labelledMaps.get(label).isEmpty()) {
deprecatedLabels.add(label);
}
}
}
for (String label : deprecatedLabels) {
labelledMaps.remove(label);
labels.remove(label);
}
}
oldOne.removeBacklink(this);
if(newOne != null) newOne.addBacklink(this);
if (modified) {
this.state = GameDataElement.State.modified;
childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(this);
}
}
@Override
@@ -154,6 +187,7 @@ public class WorldmapSegment extends GameDataElement {
map.setAttribute("x", Integer.toString(mapLocations.get(s).x + segmentX));
map.setAttribute("y", Integer.toString(mapLocations.get(s).y + segmentY));
for (String label : labelledMaps.keySet()) {
if (TEMP_LABEL_KEY.equals(label)) continue;
if (labelledMaps.get(label).contains(s)) {
map.setAttribute("area", label);
}
@@ -161,7 +195,9 @@ public class WorldmapSegment extends GameDataElement {
element.appendChild(map);
}
for (NamedArea area : labels.values()) {
for (String key : labels.keySet()) {
if (TEMP_LABEL_KEY.equals(key)) continue;
NamedArea area = labels.get(key);
Element namedArea = doc.createElement("namedarea");
namedArea.setAttribute("id", area.id);
namedArea.setAttribute("name", area.name);

View File

@@ -14,6 +14,7 @@ import java.util.Map;
import javax.imageio.ImageIO;
import javax.swing.tree.TreeNode;
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.GameSource.Type;
@@ -174,7 +175,7 @@ public class Spritesheet extends GameDataElement {
}
Image result = getImage(index);
if (result == null) return null;
result = result.getScaledInstance(16, 16, BufferedImage.SCALE_SMOOTH);
result = result.getScaledInstance((int)(16*ATContentStudio.SCALING), (int)(16*ATContentStudio.SCALING), Image.SCALE_SMOOTH);
cache_icon.put(index, result);
return result;
}

View File

@@ -167,6 +167,9 @@ public class WriterModeData extends GameDataElement {
this.id = (String) json.get("id");
this.index = ((Number)json.get("index")).intValue();
this.id_prefix = (String) json.get("id_prefix");
if (threadsNextIndex.get(id_prefix) == null || threadsNextIndex.get(id_prefix) <= index) {
threadsNextIndex.put(id_prefix, index+1);
}
this.text = (String) json.get("text");
this.dialogue_id = (String) json.get("dialogue");
if (json.get("begin") != null && ((Boolean)json.get("begin"))) begin = this;
@@ -541,6 +544,7 @@ public class WriterModeData extends GameDataElement {
nodesById.put(dialogue.getID(), dialogue);
}
}
this.state = State.parsed;
}
@@ -557,6 +561,11 @@ public class WriterModeData extends GameDataElement {
this.parse();
}
if (this.state == State.parsed) {
for (String prefix : threadsNextIndex.keySet()) {
while (getProject().getDialogue(prefix+threadsNextIndex.get(prefix)) != null) {
threadsNextIndex.put(prefix, threadsNextIndex.get(prefix)+1);
}
}
for (WriterDialogue dialogue : nodesById.values()) {
if (dialogue.dialogue_id != null) {
dialogue.dialogue = getProject().getDialogue(dialogue.dialogue_id);
@@ -571,32 +580,34 @@ public class WriterModeData extends GameDataElement {
}
}
//TODO Seriously, this is failure-prone by design. Can't do much better though...
List<Dialogue.Reply> linked = new ArrayList<Dialogue.Reply>(dialogue.dialogue.replies.size());
if (dialogue.dialogue != null && dialogue.dialogue.replies != null) {
//Try to hook to existing replies... not as easy when there's no ID.
Dialogue.Reply best = null;
int score, maxScore = 0;
for (Dialogue.Reply dReply : dialogue.dialogue.replies) {
//Never link twice to the same...
if (linked.contains(dReply)) continue;
score = 0;
//Arbitrary values... hopefully this gives good results.
//Same target gives good hope of preserving at least the structure.
if (dReply.next_phrase_id != null && dReply.next_phrase_id.equals(reply.next_dialogue_id)) score +=50;
//Same text is almost as good as an ID, but there may be duplicates due to requirements system...
if (dReply.text != null && dReply.text.equals(reply.text)) score +=40;
//Same slot in the list. That's not so bad if all else fails, and could help sort duplicates with same text.
if (dialogue.dialogue.replies.indexOf(dReply) == dialogue.replies.indexOf(reply)) score +=20;
//Both have null text. It's not much, but it's something....
if (dReply.text == null && reply.text == null) score += 10;
if (score > maxScore) {
maxScore = score;
best = dReply;
}
}
if (maxScore > 0) {
reply.reply = best;
linked.add(best);
if (dialogue.dialogue != null) {
List<Dialogue.Reply> linked = new ArrayList<Dialogue.Reply>(dialogue.dialogue.replies.size());
if (dialogue.dialogue != null && dialogue.dialogue.replies != null) {
//Try to hook to existing replies... not as easy when there's no ID.
Dialogue.Reply best = null;
int score, maxScore = 0;
for (Dialogue.Reply dReply : dialogue.dialogue.replies) {
//Never link twice to the same...
if (linked.contains(dReply)) continue;
score = 0;
//Arbitrary values... hopefully this gives good results.
//Same target gives good hope of preserving at least the structure.
if (dReply.next_phrase_id != null && dReply.next_phrase_id.equals(reply.next_dialogue_id)) score +=50;
//Same text is almost as good as an ID, but there may be duplicates due to requirements system...
if (dReply.text != null && dReply.text.equals(reply.text)) score +=40;
//Same slot in the list. That's not so bad if all else fails, and could help sort duplicates with same text.
if (dialogue.dialogue.replies.indexOf(dReply) == dialogue.replies.indexOf(reply)) score +=20;
//Both have null text. It's not much, but it's something....
if (dReply.text == null && reply.text == null) score += 10;
if (score > maxScore) {
maxScore = score;
best = dReply;
}
}
if (maxScore > 0) {
reply.reply = best;
linked.add(best);
}
}
}
}

View File

@@ -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 (needsSaving() ? "*" : "")+"Dialogue sketchs";
return (needsSaving() ? "*" : "")+"Dialogue sketches";
}
@Override

View File

@@ -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"));
}

View File

@@ -7,6 +7,7 @@ import java.util.Map;
import javax.imageio.ImageIO;
import com.gpl.rpg.atcontentstudio.ATContentStudio;
import com.gpl.rpg.atcontentstudio.Notification;
public class DefaultIcons {
@@ -107,6 +108,10 @@ public class DefaultIcons {
public static Image getNPCImage() { return getImage(NPC_RES); }
public static Image getNPCIcon() { return getIcon(NPC_RES); }
private static String BONEMEAL_RES = "/com/gpl/rpg/atcontentstudio/img/bonemeal.png";
public static Image getBonemealImage() { return getImage(BONEMEAL_RES); }
public static Image getBonemealIcon() { return getIcon(BONEMEAL_RES); }
private static String NPC_CLOSE_RES = "/com/gpl/rpg/atcontentstudio/img/npc_close.png";
public static Image getNPCCloseImage() { return getImage(NPC_CLOSE_RES); }
public static Image getNPCCloseIcon() { return getIcon(NPC_CLOSE_RES); }
@@ -130,6 +135,14 @@ public class DefaultIcons {
private static String GOLD_RES = "/com/gpl/rpg/atcontentstudio/img/ui_icon_coins.png";
public static Image getGoldImage() { return getImage(GOLD_RES); }
public static Image getGoldIcon() { return getIcon(GOLD_RES); }
private static String SKILL_RES = "/com/gpl/rpg/atcontentstudio/img/ui_icon_skill.png";
public static Image getSkillImage() { return getImage(SKILL_RES); }
public static Image getSkillIcon() { return getIcon(SKILL_RES); }
private static String IMMUNITY_RES = "/com/gpl/rpg/atcontentstudio/img/ui_icon_immunity.png";
public static Image getImmunityImage() { return getImage(IMMUNITY_RES); }
public static Image getImmunityIcon() { return getIcon(IMMUNITY_RES); }
private static String ITEM_CATEGORY_RES = "/com/gpl/rpg/atcontentstudio/img/equip_weapon.png";
public static Image getItemCategoryImage() { return getImage(ITEM_CATEGORY_RES); }
@@ -227,10 +240,43 @@ public class DefaultIcons {
public static Image getCreateTileLayerImage() { return getImage(CREATE_TILE_LAYER_RES); }
public static Image getCreateTileLayerIcon() { return getIcon(CREATE_TILE_LAYER_RES); }
private static String LABEL_RES = "/com/gpl/rpg/atcontentstudio/img/label.png";
public static Image getLabelImage() { return getImage(LABEL_RES); }
public static Image getLabelIcon() { return getIcon(LABEL_RES); }
private static String ZOOM_RES = "/com/gpl/rpg/atcontentstudio/img/zoom.png";
public static Image getZoomImage() { return getImage(ZOOM_RES); }
public static Image getZoomIcon() { return getIcon(ZOOM_RES); }
private static String TIMER_RES = "/com/gpl/rpg/atcontentstudio/img/timer.png";
public static Image getTimerImage() { return getImage(TIMER_RES); }
public static Image getTimerIcon() { return getIcon(TIMER_RES); }
private static String ALIGNMENT_RES = "/com/gpl/rpg/atcontentstudio/img/alignment.png";
public static Image getAlignmentImage() { return getImage(ALIGNMENT_RES); }
public static Image getAlignmentIcon() { return getIcon(ALIGNMENT_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 {
@@ -246,7 +292,7 @@ public class DefaultIcons {
private static Image getIcon(String res) {
if (iconCache.get(res) == null) {
Image icon = getImage(res).getScaledInstance(16, 16, Image.SCALE_SMOOTH);
Image icon = getImage(res).getScaledInstance((int)(16*ATContentStudio.SCALING), (int)(16*ATContentStudio.SCALING), Image.SCALE_SMOOTH);
iconCache.put(res, icon);
}
return iconCache.get(res);

View File

@@ -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;
@@ -42,12 +47,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 +63,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 +117,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 +255,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 +271,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);
@@ -201,11 +315,15 @@ public abstract class Editor extends JPanel implements ProjectElementListener {
// }
public static JSpinner addIntegerField(JPanel pane, String label, Integer initialValue, boolean allowNegatives, boolean editable, final FieldUpdateListener listener) {
return addIntegerField(pane, label, initialValue, 0, allowNegatives, editable, listener);
}
public static JSpinner addIntegerField(JPanel pane, String label, Integer initialValue, Integer defaultValue, boolean allowNegatives, boolean editable, final FieldUpdateListener listener) {
JPanel tfPane = new JPanel();
tfPane.setLayout(new JideBoxLayout(tfPane, JideBoxLayout.LINE_AXIS, 6));
JLabel tfLabel = new JLabel(label);
tfPane.add(tfLabel, JideBoxLayout.FIX);
final JSpinner spinner = new JSpinner(new SpinnerNumberModel(initialValue != null ? initialValue.intValue() : 0, allowNegatives ? Integer.MIN_VALUE : 0, Integer.MAX_VALUE, 1));
final JSpinner spinner = new JSpinner(new SpinnerNumberModel(initialValue != null ? initialValue.intValue() : defaultValue.intValue(), allowNegatives ? Integer.MIN_VALUE : 0, Integer.MAX_VALUE, 1));
((JSpinner.DefaultEditor)spinner.getEditor()).getTextField().setHorizontalAlignment(JTextField.LEFT);
spinner.setEnabled(editable);
((DefaultFormatter)((NumberEditor)spinner.getEditor()).getTextField().getFormatter()).setCommitsOnValidEdit(true);
@@ -534,8 +652,63 @@ public abstract class Editor extends JPanel implements ProjectElementListener {
return gdeBox;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public JComboBox<QuestStage> addQuestStageBox(JPanel pane, Project proj, String label, Integer initialValue, boolean writable, final FieldUpdateListener listener, Quest quest, @SuppressWarnings("rawtypes") 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"})
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 +729,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 +807,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 +838,64 @@ 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);
}
@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 +1029,5 @@ public abstract class Editor extends JPanel implements ProjectElementListener {
return null;
}
}

View File

@@ -0,0 +1,226 @@
package com.gpl.rpg.atcontentstudio.ui;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import com.gpl.rpg.atcontentstudio.ATContentStudio;
import com.gpl.rpg.atcontentstudio.model.Project;
import com.gpl.rpg.atcontentstudio.model.gamedata.GameDataSet;
import com.gpl.rpg.atcontentstudio.model.maps.TMXMapSet;
import com.gpl.rpg.atcontentstudio.model.sprites.SpriteSheetSet;
import com.jidesoft.swing.JideBoxLayout;
public class ExportProjectWizard extends JDialog {
private static final long serialVersionUID = -8745083621008868612L;
JPanel pane;
JLabel errorLabel, fileSelectionLabel;
JRadioButton asZip, overSources;
JComboBox<String> target;
JButton browse;
JButton okButton, cancelButton;
Project proj;
public ExportProjectWizard(Project proj) {
super(ATContentStudio.frame);
setTitle("Export project for inclusion in-game");
this.proj = proj;
pane = new JPanel();
pane.setLayout(new JideBoxLayout(pane, JideBoxLayout.PAGE_AXIS, 6));
errorLabel = new JLabel();
pane.add(errorLabel, JideBoxLayout.FIX);
pane.add(new JLabel("Export this ATCS project..."), JideBoxLayout.FIX);
ButtonGroup radioGroup = new ButtonGroup();
asZip = new JRadioButton("... as a Zip archive");
radioGroup.add(asZip);
overSources = new JRadioButton("... into a game source folder");
radioGroup.add(overSources);
asZip.setSelected(true);
pane.add(asZip, JideBoxLayout.FIX);
pane.add(overSources, JideBoxLayout.FIX);
ActionListener updateListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
updateState();
}
};
asZip.addActionListener(updateListener);
overSources.addActionListener(updateListener);
target = new JComboBox<String>();
target.setEditable(true);
target.addActionListener(updateListener);
browse = new JButton("Browse");
browse.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JFileChooser jfc = new JFileChooser(){
private static final long serialVersionUID = -3001082967957619011L;
@Override
public boolean accept(File f) {
if (asZip.isSelected()) {
if (f.isDirectory() || f.getName().endsWith(".zip") || f.getName().endsWith(".ZIP")) {
return super.accept(f);
} else {
return false;
}
} else {
return f.isDirectory();
}
}
};
jfc.setFileSelectionMode(asZip.isSelected() ? JFileChooser.FILES_AND_DIRECTORIES : JFileChooser.DIRECTORIES_ONLY);
jfc.setSelectedFile(new File(target.getSelectedItem() == null ? "" : target.getSelectedItem().toString()));
jfc.setMultiSelectionEnabled(false);
int result = jfc.showOpenDialog(ATContentStudio.frame);
if (result == JFileChooser.APPROVE_OPTION) {
File f = jfc.getSelectedFile();
if (asZip.isSelected() && !f.getAbsolutePath().substring(f.getAbsolutePath().length() - 4, f.getAbsolutePath().length()).equalsIgnoreCase(".zip")) {
f = new File(f.getAbsolutePath()+".zip");
}
target.setSelectedItem(f.getAbsolutePath());
updateState();
}
}
});
JPanel fileSelectionPane = new JPanel();
fileSelectionPane.setLayout(new JideBoxLayout(fileSelectionPane, JideBoxLayout.LINE_AXIS, 6));
fileSelectionLabel = new JLabel("Zip file: ");
fileSelectionPane.add(fileSelectionLabel, JideBoxLayout.FIX);
fileSelectionPane.add(target, JideBoxLayout.VARY);
fileSelectionPane.add(browse, JideBoxLayout.FIX);
pane.add(fileSelectionPane, JideBoxLayout.FIX);
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new JideBoxLayout(buttonPane, JideBoxLayout.LINE_AXIS, 6));
buttonPane.add(new JPanel(), JideBoxLayout.VARY);
cancelButton = new JButton("Cancel");
buttonPane.add(cancelButton, JideBoxLayout.FIX);
okButton = new JButton("Ok");
buttonPane.add(okButton, JideBoxLayout.FIX);
pane.add(new JPanel(), JideBoxLayout.VARY);
pane.add(buttonPane, JideBoxLayout.FIX);
cancelButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ExportProjectWizard.this.setVisible(false);
ExportProjectWizard.this.dispose();
}
});
okButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (asZip.isSelected()) {
ExportProjectWizard.this.proj.exportProjectAsZipPackage(new File(target.getSelectedItem().toString()));
} else {
ExportProjectWizard.this.proj.exportProjectOverGameSource(new File(target.getSelectedItem().toString()));
}
ExportProjectWizard.this.setVisible(false);
ExportProjectWizard.this.dispose();
}
});
updateState();
getContentPane().setLayout(new BorderLayout());
getContentPane().add(pane, BorderLayout.CENTER);
setMinimumSize(new Dimension(500,150));
pack();
Dimension sdim = Toolkit.getDefaultToolkit().getScreenSize();
Dimension wdim = getSize();
setLocation((sdim.width - wdim.width)/2, (sdim.height - wdim.height)/2);
}
private void updateState() {
if (asZip.isSelected()) {
fileSelectionLabel.setText("Zip file: ");
} else {
fileSelectionLabel.setText("Game source folder: ");
}
File f = new File(target.getSelectedItem() == null ? "" : target.getSelectedItem().toString());
if (asZip.isSelected()) {
if (target.getSelectedItem() == null || target.getSelectedItem().toString().length() <= 0) {
errorLabel.setText("<html><font color=\"#FF0000\">You must select where to save the zip file.</font></html>");
okButton.setEnabled(false);
} else if (f.isDirectory()) {
errorLabel.setText("<html><font color=\"#FF0000\">The selected target is a directory. It should be a zip file.</font></html>");
okButton.setEnabled(false);
} else if (!(f.getName().toLowerCase().endsWith(".zip"))) {
errorLabel.setText("<html><font color=\"#FF0000\">The selected target is not a zip file. It should be a zip file.</font></html>");
okButton.setEnabled(false);
} else if (f.exists()) {
errorLabel.setText("<html><font color=\"#FF9000\">The selected target is an existing zip file. It will be overwritten.</font></html>");
okButton.setEnabled(true);
} else {
errorLabel.setText("<html><font color=\"#00AA00\">Everything looks good !</font></html>");
okButton.setEnabled(true);
}
} else {
if (target.getSelectedItem() == null || target.getSelectedItem().toString().length() <= 0) {
errorLabel.setText("<html><font color=\"#FF0000\">You must select an AT source root folder.</font></html>");
okButton.setEnabled(false);
} else if (!f.isDirectory() || !f.exists()) {
errorLabel.setText("<html><font color=\"#FF0000\">The selected AT source is not a folder. It should be an existing AT source root folder.</font></html>");
okButton.setEnabled(false);
} else {
File res = new File(f, GameDataSet.DEFAULT_REL_PATH_IN_SOURCE);
File drawable = new File(f, SpriteSheetSet.DEFAULT_REL_PATH_IN_SOURCE);
File xml = new File(f, TMXMapSet.DEFAULT_REL_PATH_IN_SOURCE);
if (!res.exists()) {
errorLabel.setText("<html><font color=\"#FF9000\">The selected AT source root folder does not contain the \"res\" folder.</font></html>");
okButton.setEnabled(true);
} else if (!drawable.exists()) {
errorLabel.setText("<html><font color=\"#FF9000\">The selected AT source root folder does not contain the \"drawable\" folder.</font></html>");
okButton.setEnabled(true);
} else if (!xml.exists()) {
errorLabel.setText("<html><font color=\"#FF9000\">The selected AT source root folder does not contain the \"xml\" folder.</font></html>");
okButton.setEnabled(true);
} else {
errorLabel.setText("<html><font color=\"#00AA00\">Everything looks good !</font></html>");
okButton.setEnabled(true);
}
}
}
revalidate();
repaint();
}
}

View File

@@ -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;

View File

@@ -338,6 +338,7 @@ public class JSONImportWizard extends JDialog {
} else if (existingNode.getDataType() == GameSource.Type.altered) {
errors.add("An item with id "+node.id+" is already altered in this project.");
} else {
node.jsonFile = existingNode.jsonFile;
warnings.add("An item with id "+node.id+" exists in the used game source. This one will be inserted as \"altered\"");
}
existingNode = null;
@@ -412,7 +413,7 @@ public class JSONImportWizard extends JDialog {
proj.createElements(created);
JSONElement lastNode = created.get(created.size() - 1);
if (lastNode != null) {
lastNode.save();
// lastNode.save();
ATContentStudio.frame.selectInTree(lastNode);
}
JSONImportWizard.this.setVisible(false);

View File

@@ -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;
@@ -21,6 +18,7 @@ import javax.swing.ListModel;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import com.gpl.rpg.atcontentstudio.ATContentStudio;
import com.gpl.rpg.atcontentstudio.Notification;
import com.gpl.rpg.atcontentstudio.NotificationListener;
@@ -30,22 +28,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()));
}
@@ -66,7 +55,7 @@ public class NotificationsPane extends JList {
label.setBorder(BorderFactory.createLineBorder(Color.BLUE));
// label.setForeground(Color.WHITE);
}
f = f.deriveFont(10f);
f = f.deriveFont(10f*ATContentStudio.SCALING);
label.setFont(f);
return label;
}

View File

@@ -0,0 +1,35 @@
package com.gpl.rpg.atcontentstudio.ui;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.Icon;
public class OverlayIcon implements Icon {
private Image background;
private Image overlay;
public OverlayIcon(Image background, Image overlay) {
this.background = background;
this.overlay = overlay;
}
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
g.drawImage(background, x, y, null);
g.drawImage(overlay, x, y, null);
}
@Override
public int getIconWidth() {
return Math.max(background.getWidth(null), overlay.getWidth(null));
}
@Override
public int getIconHeight() {
return Math.max(background.getHeight(null), overlay.getHeight(null));
}
}

View File

@@ -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;

View File

@@ -140,14 +140,14 @@ public class ProjectsTree extends JPanel {
projectsTree.addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {
List<TreePath> newPaths = new ArrayList<TreePath>();
for (TreePath path : e.getPaths()) {
if (e.isAddedPath(path)) newPaths.add(path);
}
// List<TreePath> newPaths = new ArrayList<TreePath>();
// for (TreePath path : e.getPaths()) {
// if (e.isAddedPath(path)) newPaths.add(path);
// }
if (e.getPath() == null) {
ATContentStudio.frame.actions.selectionChanged(null, newPaths.toArray(new TreePath[newPaths.size()]));
ATContentStudio.frame.actions.selectionChanged(null, projectsTree.getSelectionPaths());
} else {
ATContentStudio.frame.actions.selectionChanged((ProjectTreeNode) e.getPath().getLastPathComponent(), newPaths.toArray(new TreePath[newPaths.size()]));
ATContentStudio.frame.actions.selectionChanged((ProjectTreeNode) e.getPath().getLastPathComponent(), projectsTree.getSelectionPaths());
}
}
});
@@ -242,17 +242,17 @@ public class ProjectsTree extends JPanel {
addNextSeparator = false;
}
if (actions.testWriter.isEnabled()) {
if (actions.createWriter.isEnabled()) {
addNextSeparator = true;
popupMenu.add(new JMenuItem(actions.testWriter));
popupMenu.add(new JMenuItem(actions.createWriter));
}
// if (actions.testCommitWriter.isEnabled()) {
// addNextSeparator = true;
// popupMenu.add(new JMenuItem(actions.testCommitWriter));
// }
if (actions.createWriter.isEnabled()) {
if (actions.generateWriter.isEnabled()) {
addNextSeparator = true;
popupMenu.add(new JMenuItem(actions.createWriter));
popupMenu.add(new JMenuItem(actions.generateWriter));
}
if (addNextSeparator) {
popupMenu.add(new JSeparator());

View File

@@ -40,7 +40,7 @@ public class StudioFrame extends JFrame {
private static final long serialVersionUID = -3391514100319186661L;
final ProjectsTree projectTree;
final EditorsArea editors;
@@ -165,6 +165,7 @@ public class StudioFrame extends JFrame {
public void actionPerformed(ActionEvent e) {
try {
UIManager.setLookAndFeel(i.getClassName());
ATContentStudio.scaleUIFont();
SwingUtilities.updateComponentTreeUI(ATContentStudio.frame);
ConfigCache.setFavoriteLaFClassName(i.getClassName());
} catch (ClassNotFoundException e1) {

View File

@@ -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;

View File

@@ -9,6 +9,7 @@ import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import com.gpl.rpg.atcontentstudio.ATContentStudio;
import com.jidesoft.swing.JideBoxLayout;
@@ -19,7 +20,7 @@ private static final long serialVersionUID = 8239669104275145995L;
super(parent, "Loading...");
this.setIconImage(DefaultIcons.getMainIconImage());
this.getContentPane().setLayout(new JideBoxLayout(this.getContentPane(), JideBoxLayout.PAGE_AXIS, 6));
this.getContentPane().add(new JLabel("<html><font size=5>Please wait.<br/>"+message+"</font></html>"), JideBoxLayout.VARY);
this.getContentPane().add(new JLabel("<html><font size="+(int)(5 * ATContentStudio.SCALING)+">Please wait.<br/>"+message+"</font></html>"), JideBoxLayout.VARY);
JMovingIdler idler = new JMovingIdler();
idler.setBackground(Color.WHITE);
idler.setForeground(Color.GREEN);
@@ -46,7 +47,7 @@ private static final long serialVersionUID = 8239669104275145995L;
info.setVisible(true);
workload.run();
info.dispose();
if (showConfirm) JOptionPane.showMessageDialog(parent, "<html><font size=5>Done !</font></html>");
if (showConfirm) JOptionPane.showMessageDialog(parent, "<html><font size="+(int)(5 * ATContentStudio.SCALING)+">Done !</font></html>");
};
}.start();
}

View File

@@ -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;
@@ -32,8 +31,11 @@ import com.gpl.rpg.atcontentstudio.model.Workspace;
import com.gpl.rpg.atcontentstudio.model.gamedata.Dialogue;
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.TMXMapSet;
import com.gpl.rpg.atcontentstudio.model.maps.Worldmap;
import com.gpl.rpg.atcontentstudio.model.maps.WorldmapSegment;
import com.gpl.rpg.atcontentstudio.model.saves.SavedGamesSet;
import com.gpl.rpg.atcontentstudio.model.tools.writermode.WriterModeData;
import com.gpl.rpg.atcontentstudio.model.tools.writermode.WriterModeDataSet;
@@ -57,6 +59,7 @@ public class WorkspaceActions {
public void actionPerformed(ActionEvent e) {
if (!(selectedNode instanceof Project)) return;
Workspace.closeProject((Project) selectedNode);
selectedNode = null;
};
public void selectionChanged(ProjectTreeNode selectedNode, TreePath[] selectedPaths) {
setEnabled(selectedNode instanceof Project);
@@ -123,19 +126,45 @@ public class WorkspaceActions {
ATContentStudio.frame.closeEditor(element);
element.childrenRemoved(new ArrayList<ProjectTreeNode>());
if (element instanceof JSONElement) {
@SuppressWarnings("unchecked")
GameDataCategory<JSONElement> category = (GameDataCategory<JSONElement>) element.getParent();
category.remove(element);
if (impactedCategories.get(category) == null) {
impactedCategories.put(category, new HashSet<File>());
if (element.getParent() instanceof GameDataCategory<?>) {
@SuppressWarnings("unchecked")
GameDataCategory<JSONElement> category = (GameDataCategory<JSONElement>) element.getParent();
category.remove(element);
if (impactedCategories.get(category) == null) {
impactedCategories.put(category, new HashSet<File>());
}
GameDataElement newOne = element.getProject().getGameDataElement(((JSONElement)element).getClass(), element.id);
if (element instanceof Quest) {
for (QuestStage oldStage : ((Quest) element).stages) {
QuestStage newStage = newOne != null ? ((Quest) newOne).getStage(oldStage.progress) : null;
for (GameDataElement backlink : oldStage.getBacklinks()) {
backlink.elementChanged(oldStage, newStage);
}
}
}
for (GameDataElement backlink : element.getBacklinks()) {
backlink.elementChanged(element, newOne);
}
impactedCategories.get(category).add(((JSONElement) element).jsonFile);
}
impactedCategories.get(category).add(((JSONElement) element).jsonFile);
} else if (element instanceof TMXMap) {
TMXMapSet parent = (TMXMapSet) element.getParent();
parent.tmxMaps.remove(element);
((TMXMap)element).delete();
GameDataElement newOne = element.getProject().getMap(element.id);
for (GameDataElement backlink : element.getBacklinks()) {
backlink.elementChanged(element, newOne);
}
} else if (element instanceof WriterModeData) {
WriterModeDataSet parent = (WriterModeDataSet) element.getParent();
parent.writerModeDataList.remove(element);
} else if (element instanceof WorldmapSegment) {
if (element.getParent() instanceof Worldmap) {
((Worldmap)element.getParent()).remove(element);
element.save();
for (GameDataElement backlink : element.getBacklinks()) {
backlink.elementChanged(element, element.getProject().getWorldmapSegment(element.id));
}
}
}
}
new Thread() {
@@ -166,20 +195,52 @@ public class WorkspaceActions {
@Override
public void run() {
node.childrenRemoved(new ArrayList<ProjectTreeNode>());
if (node.getParent() instanceof GameDataCategory<?>) {
((GameDataCategory<?>)node.getParent()).remove(node);
List<SaveEvent> events = node.attemptSave();
if (events == null || events.isEmpty()) {
node.save();
} else {
new SaveItemsWizard(events, null).setVisible(true);
if (node instanceof JSONElement) {
if (node.getParent() instanceof GameDataCategory<?>) {
((GameDataCategory<?>)node.getParent()).remove(node);
List<SaveEvent> events = node.attemptSave();
if (events == null || events.isEmpty()) {
node.save();
} else {
new SaveItemsWizard(events, null).setVisible(true);
}
GameDataElement newOne = node.getProject().getGameDataElement(((JSONElement)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, newOne);
}
}
// ((GameDataCategory<?>)node.getParent()).remove(node);
// List<SaveEvent> events = node.attemptSave();
// if (events == null || events.isEmpty()) {
// node.save();
// } else {
// new SaveItemsWizard(events, null).setVisible(true);
// }
} else if (node instanceof TMXMap) {
TMXMapSet parent = (TMXMapSet) node.getParent();
parent.tmxMaps.remove(node);
((TMXMap)node).delete();
GameDataElement newOne = node.getProject().getMap(node.id);
for (GameDataElement backlink : node.getBacklinks()) {
backlink.elementChanged(node, newOne);
}
} else if (node instanceof WriterModeData) {
WriterModeDataSet parent = (WriterModeDataSet) node.getParent();
parent.writerModeDataList.remove(node);
} else if (node instanceof WorldmapSegment) {
if (node.getParent() instanceof Worldmap) {
((Worldmap)node.getParent()).remove(node);
node.save();
for (GameDataElement backlink : node.getBacklinks()) {
backlink.elementChanged(node, node.getProject().getWorldmapSegment(node.id));
}
}
}
}
}.start();
@@ -193,12 +254,9 @@ public class WorkspaceActions {
for (TreePath selected : selectedPaths) {
if (selected.getLastPathComponent() instanceof GameDataElement && ((GameDataElement)selected.getLastPathComponent()).writable) {
elementsToDelete.add((GameDataElement) selected.getLastPathComponent());
multiMode = true;
} else {
multiMode = false;
break;
}
}
multiMode = elementsToDelete.size() > 1;
putValue(Action.NAME, "Delete all selected elements");
setEnabled(multiMode);
} else if (selectedNode instanceof GameDataElement && ((GameDataElement)selectedNode).writable) {
@@ -295,21 +353,7 @@ public class WorkspaceActions {
public ATCSAction exportProject = new ATCSAction("Export project", "Generates a zip file containing all the created & altered resources of the project, ready to merge with the game source."){
public void actionPerformed(ActionEvent e) {
if (selectedNode == null || selectedNode.getProject() == null) return;
JFileChooser chooser = new JFileChooser() {
private static final long serialVersionUID = 8039332384370636746L;
public boolean accept(File f) {
return f.isDirectory() || f.getName().endsWith(".zip") || f.getName().endsWith(".ZIP");
}
};
chooser.setMultiSelectionEnabled(false);
int result = chooser.showSaveDialog(ATContentStudio.frame);
if (result == JFileChooser.APPROVE_OPTION) {
File f = chooser.getSelectedFile();
if (!f.getAbsolutePath().substring(f.getAbsolutePath().length() - 4, f.getAbsolutePath().length()).equalsIgnoreCase(".zip")) {
f = new File(f.getAbsolutePath()+".zip");
}
selectedNode.getProject().generateExportPackage(f);
}
new ExportProjectWizard(selectedNode.getProject()).setVisible(true);
};
public void selectionChanged(ProjectTreeNode selectedNode, TreePath[] selectedPaths) {
setEnabled(selectedNode != null && selectedNode.getProject() != null);
@@ -341,7 +385,7 @@ public class WorkspaceActions {
};
};
public ATCSAction testWriter = new ATCSAction("Create dialogue sketch", "Create a dialogue sketch for fast dialogue edition"){
public ATCSAction createWriter = new ATCSAction("Create dialogue sketch", "Create a dialogue sketch for fast dialogue edition"){
public void actionPerformed(ActionEvent e) {
if (selectedNode == null || selectedNode.getProject() == null) return;
new WriterSketchCreationWizard(selectedNode.getProject()).setVisible(true);
@@ -376,7 +420,7 @@ public class WorkspaceActions {
}
};*/
public ATCSAction createWriter = new ATCSAction("Generate dialogue sketch", "Generates a dialogue sketch from this dialogue and its tree.") {
public ATCSAction generateWriter = new ATCSAction("Generate dialogue sketch", "Generates a dialogue sketch from this dialogue and its tree.") {
public void actionPerformed(ActionEvent e) {
if (selectedNode == null || selectedNode.getProject() == null || !(selectedNode instanceof Dialogue)) return;
new WriterSketchCreationWizard(selectedNode.getProject(), (Dialogue)selectedNode).setVisible(true);
@@ -414,9 +458,9 @@ public class WorkspaceActions {
actions.add(exportProject);
actions.add(showAbout);
actions.add(exitATCS);
actions.add(testWriter);
// actions.add(testCommitWriter);
actions.add(createWriter);
// actions.add(testCommitWriter);
actions.add(generateWriter);
actions.add(editWorkspaceSettings);
selectionChanged(null, null);
}

View File

@@ -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,11 @@ public class WorkspaceSettingsEditor extends JDialog {
JRadioButton useSystemDefaultImageViewerButton, useSystemDefaultImageEditorButton, useCustomImageEditorButton;
JTextField imageEditorCommandField;
JCheckBox useInternetBox;
JCheckBox translatorModeBox;
JComboBox<String> translatorLanguagesBox;
JCheckBox checkUpdatesBox;
public WorkspaceSettingsEditor(WorkspaceSettings settings) {
@@ -46,6 +54,7 @@ public class WorkspaceSettingsEditor extends JDialog {
pane.add(getExternalToolsPane(), JideBoxLayout.FIX);
pane.add(getInternetPane(), JideBoxLayout.FIX);
pane.add(new JPanel(), JideBoxLayout.VARY);
buttonPane.add(new JPanel(), JideBoxLayout.VARY);
@@ -147,6 +156,42 @@ public class WorkspaceSettingsEditor extends JDialog {
return pane;
}
public JPanel getInternetPane() {
CollapsiblePanel pane = new CollapsiblePanel("Internet options");
pane.setLayout(new JideBoxLayout(pane, JideBoxLayout.PAGE_AXIS));
useInternetBox = new JCheckBox("Allow connecting to internet to retrieve data from weblate and check for updates.");
pane.add(useInternetBox, JideBoxLayout.FIX);
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);
checkUpdatesBox = new JCheckBox("Check for ATCS updates at startup");
pane.add(checkUpdatesBox, JideBoxLayout.FIX);
useInternetBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
translatorLanguagesBox.setEnabled(useInternetBox.isSelected());
translatorModeBox.setEnabled(useInternetBox.isSelected());
checkUpdatesBox.setEnabled(useInternetBox.isSelected());
}
});
return pane;
}
public void loadFromModel() {
//Tiled
useSystemDefaultMapEditorButton.setSelected(settings.useSystemDefaultMapEditor.getCurrentValue());
@@ -157,6 +202,20 @@ public class WorkspaceSettingsEditor extends JDialog {
useSystemDefaultImageEditorButton.setSelected(settings.useSystemDefaultImageEditor.getCurrentValue());
useCustomImageEditorButton.setSelected(!(settings.useSystemDefaultImageViewer.getCurrentValue() || settings.useSystemDefaultImageEditor.getCurrentValue()));
imageEditorCommandField.setText(settings.imageEditorCommand.getCurrentValue());
//Internet
useInternetBox.setSelected(settings.useInternet.getCurrentValue());
if (settings.translatorLanguage.getCurrentValue() != null) {
translatorModeBox.setSelected(true);
translatorLanguagesBox.setSelectedItem(settings.translatorLanguage.getCurrentValue());
translatorLanguagesBox.setEnabled(useInternetBox.isSelected());
} else {
translatorModeBox.setSelected(false);
translatorLanguagesBox.setSelectedItem(null);
translatorLanguagesBox.setEnabled(false);
}
translatorModeBox.setEnabled(useInternetBox.isSelected());
checkUpdatesBox.setSelected(settings.checkUpdates.getCurrentValue());
checkUpdatesBox.setEnabled(useInternetBox.isSelected());
}
public void pushToModel() {
@@ -167,7 +226,14 @@ public class WorkspaceSettingsEditor extends JDialog {
settings.useSystemDefaultImageViewer.setCurrentValue(useSystemDefaultImageViewerButton.isSelected());
settings.useSystemDefaultImageEditor.setCurrentValue(useSystemDefaultImageEditorButton.isSelected());
settings.imageEditorCommand.setCurrentValue(imageEditorCommandField.getText());
//Internet
settings.useInternet.setCurrentValue(useInternetBox.isSelected());
if (translatorModeBox.isSelected()) {
settings.translatorLanguage.setCurrentValue((String)translatorLanguagesBox.getSelectedItem());
} else {
settings.translatorLanguage.resetDefault();
}
settings.checkUpdates.setCurrentValue(checkUpdatesBox.isSelected());
settings.save();
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -35,13 +35,17 @@ public class ActorConditionEditor extends JSONElementEditor {
private IntegerBasedCheckBox positiveBox;
private IntegerBasedCheckBox stackingBox;
private JTextField roundVisualField;
//private JTextField roundVisualField;
@SuppressWarnings("rawtypes")
private JComboBox roundVisualField;
private JSpinner roundHpMinField;
private JSpinner roundHpMaxField;
private JSpinner roundApMinField;
private JSpinner roundApMaxField;
private JTextField fullRoundVisualField;
//private JTextField fullRoundVisualField;
@SuppressWarnings("rawtypes")
private JComboBox fullRoundVisualField;
private JSpinner fullRoundHpMinField;
private JSpinner fullRoundHpMaxField;
private JSpinner fullRoundApMinField;
@@ -76,13 +80,13 @@ 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);
CollapsiblePanel roundEffectPane = new CollapsiblePanel("Effect every round (4s): ");
CollapsiblePanel roundEffectPane = new CollapsiblePanel("Effect every round (6s): ");
roundEffectPane.setLayout(new JideBoxLayout(roundEffectPane, JideBoxLayout.PAGE_AXIS));
final ActorCondition.RoundEffect roundEffect;
if (ac.round_effect != null) {
@@ -90,7 +94,7 @@ public class ActorConditionEditor extends JSONElementEditor {
} else {
roundEffect = new ActorCondition.RoundEffect();
}
roundVisualField = addTextField(roundEffectPane, "Visual effect ID: ", roundEffect.visual_effect, ac.writable, listener);
roundVisualField = addEnumValueBox(roundEffectPane, "Visual effect ID:", ActorCondition.VisualEffectID.values(), roundEffect.visual_effect, ac.writable, listener);//addTextField(roundEffectPane, "Visual effect ID: ", roundEffect.visual_effect, ac.writable, listener);
roundHpMinField = addIntegerField(roundEffectPane, "HP Bonus Min: ", roundEffect.hp_boost_min, true, ac.writable, listener);
roundHpMaxField = addIntegerField(roundEffectPane, "HP Bonus Max: ", roundEffect.hp_boost_max, true, ac.writable, listener);
roundApMinField = addIntegerField(roundEffectPane, "AP Bonus Min: ", roundEffect.ap_boost_min, true, ac.writable, listener);
@@ -99,7 +103,7 @@ public class ActorConditionEditor extends JSONElementEditor {
pane.add(roundEffectPane, JideBoxLayout.FIX);
CollapsiblePanel fullRoundEffectPane = new CollapsiblePanel("Effect every full round (20s): ");
CollapsiblePanel fullRoundEffectPane = new CollapsiblePanel("Effect every full round (25s): ");
fullRoundEffectPane.setLayout(new JideBoxLayout(fullRoundEffectPane, JideBoxLayout.PAGE_AXIS));
final ActorCondition.RoundEffect fullRoundEffect;
if (ac.full_round_effect != null) {
@@ -107,7 +111,7 @@ public class ActorConditionEditor extends JSONElementEditor {
} else {
fullRoundEffect = new ActorCondition.RoundEffect();
}
fullRoundVisualField = addTextField(fullRoundEffectPane, "Visual effect ID: ", fullRoundEffect.visual_effect, ac.writable, listener);
fullRoundVisualField = addEnumValueBox(fullRoundEffectPane, "Visual effect ID:", ActorCondition.VisualEffectID.values(), fullRoundEffect.visual_effect, ac.writable, listener);//addTextField(fullRoundEffectPane, "Visual effect ID: ", fullRoundEffect.visual_effect, ac.writable, listener);
fullRoundHpMinField = addIntegerField(fullRoundEffectPane, "HP Bonus min: ", fullRoundEffect.hp_boost_min, true, ac.writable, listener);
fullRoundHpMaxField = addIntegerField(fullRoundEffectPane, "HP Bonus max: ", fullRoundEffect.hp_boost_max, true, ac.writable, listener);
fullRoundApMinField = addIntegerField(fullRoundEffectPane, "AP Bonus min: ", fullRoundEffect.ap_boost_min, true, ac.writable, listener);
@@ -194,7 +198,7 @@ public class ActorConditionEditor extends JSONElementEditor {
if (aCond.round_effect == null) {
aCond.round_effect = new ActorCondition.RoundEffect();
}
aCond.round_effect.visual_effect = (String) value;
aCond.round_effect.visual_effect = (ActorCondition.VisualEffectID) value;
}
} else if (source == roundHpMinField) {
if (value == null) {
@@ -264,7 +268,7 @@ public class ActorConditionEditor extends JSONElementEditor {
if (aCond.full_round_effect == null) {
aCond.full_round_effect = new ActorCondition.RoundEffect();
}
aCond.full_round_effect.visual_effect = (String) value;
aCond.full_round_effect.visual_effect = (ActorCondition.VisualEffectID) value;
}
} else if (source == fullRoundHpMinField) {
if (value == null) {

View File

@@ -14,6 +14,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;
@@ -22,6 +23,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.JTextArea;
@@ -43,12 +45,14 @@ 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;
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.OverlayIcon;
import com.gpl.rpg.atcontentstudio.ui.gamedataeditors.dialoguetree.DialogueGraphView;
import com.jidesoft.swing.JideBoxLayout;
@@ -92,9 +96,13 @@ public class DialogueEditor extends JSONElementEditor {
private JPanel rewardsParamsPane;
private MyComboBox rewardMap;
private JTextField rewardObjId;
@SuppressWarnings("rawtypes")
private JComboBox rewardObjIdCombo;
private MyComboBox rewardObj;
private JSpinner rewardValue;
private JComponent rewardValue;
private JRadioButton rewardConditionTimed;
private JRadioButton rewardConditionForever;
private JRadioButton rewardConditionClear;
private RepliesListModel repliesListModel;
@SuppressWarnings("rawtypes")
@@ -113,8 +121,10 @@ public class DialogueEditor extends JSONElementEditor {
private JComboBox requirementTypeCombo;
private JPanel requirementParamsPane;
private MyComboBox requirementObj;
@SuppressWarnings("rawtypes")
private JComboBox requirementSkill;
private JTextField requirementObjId;
private JSpinner requirementValue;
private JComponent requirementValue;
private BooleanBasedCheckBox requirementNegated;
private DialogueGraphView dialogueGraphView;
@@ -165,7 +175,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: ");
@@ -356,6 +366,7 @@ public class DialogueEditor extends JSONElementEditor {
if (rewardObj != null) {
removeElementListener(rewardObj);
}
boolean immunity = false;
if (reward.type != null) {
switch (reward.type) {
case activateMapObjectGroup:
@@ -382,14 +393,62 @@ public class DialogueEditor extends JSONElementEditor {
rewardObj = null;
rewardValue = null;
break;
case actorConditionImmunity:
immunity = true;
case actorCondition:
rewardMap = null;
rewardObjId = null;
rewardObjIdCombo = null;
rewardObj = addActorConditionBox(pane, ((Dialogue)target).getProject(), "Actor Condition: ", (ActorCondition) reward.reward_obj, writable, listener);
rewardValue = addIntegerField(pane, "Duration: ", reward.reward_value, false, writable, listener);
rewardConditionTimed = new JRadioButton("For a number of rounds");
pane.add(rewardConditionTimed, JideBoxLayout.FIX);
rewardValue = addIntegerField(pane, "Duration: ", reward.reward_value, 1, false, writable, listener);
rewardConditionForever = new JRadioButton("Forever");
pane.add(rewardConditionForever, JideBoxLayout.FIX);
if (!immunity) {
rewardConditionClear = new JRadioButton("Clear actor condition");
pane.add(rewardConditionClear, JideBoxLayout.FIX);
}
ButtonGroup radioGroup = new ButtonGroup();
radioGroup.add(rewardConditionTimed);
radioGroup.add(rewardConditionForever);
if (!immunity) radioGroup.add(rewardConditionClear);
if (immunity) {
rewardConditionTimed.setSelected(reward.reward_value == null || (reward.reward_value != ActorCondition.DURATION_FOREVER && reward.reward_value != ActorCondition.MAGNITUDE_CLEAR));
rewardConditionForever.setSelected(reward.reward_value != null && reward.reward_value != ActorCondition.DURATION_FOREVER);
rewardConditionClear.setSelected(reward.reward_value != null && reward.reward_value != ActorCondition.MAGNITUDE_CLEAR);
} else {
rewardConditionTimed.setSelected(reward.reward_value != null && reward.reward_value != ActorCondition.DURATION_FOREVER);
rewardConditionForever.setSelected(reward.reward_value == null || reward.reward_value == ActorCondition.DURATION_FOREVER);
}
rewardValue.setEnabled(rewardConditionTimed.isSelected());
rewardConditionTimed.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
listener.valueChanged(rewardConditionTimed, new Boolean(rewardConditionTimed.isSelected()));
}
});
rewardConditionForever.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
listener.valueChanged(rewardConditionForever, new Boolean(rewardConditionForever.isSelected()));
}
});
if (!immunity) {
rewardConditionClear.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
listener.valueChanged(rewardConditionClear, new Boolean(rewardConditionClear.isSelected()));
}
});
}
break;
case alignmentChange:
case alignmentSet:
rewardMap = null;
rewardObjId = addTextField(pane, "Faction: ", reward.reward_obj_id, writable, listener);
rewardObjIdCombo = null;
@@ -414,19 +473,24 @@ public class DialogueEditor extends JSONElementEditor {
rewardMap = null;
rewardObjId = null;
rewardObj = addItemBox(pane, ((Dialogue)target).getProject(), "Item: ", (Item) reward.reward_obj, writable, listener);
rewardValue = addIntegerField(pane, "Quantity: ", reward.reward_value, false, writable, listener);
rewardValue = addIntegerField(pane, "Quantity: ", reward.reward_value, true, writable, listener);
break;
case removeQuestProgress:
case questProgress:
rewardMap = null;
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:
Requirement.SkillID skillId = null;
try {
skillId = reward.reward_obj_id == null ? null : Requirement.SkillID.valueOf(reward.reward_obj_id);
} catch(IllegalArgumentException e) {}
rewardMap = null;
rewardObjId = addTextField(pane, "Skill ID: ", reward.reward_obj_id, writable, listener);
rewardObjIdCombo = null;
rewardObjId = null;// addTextField(pane, "Skill ID: ", reward.reward_obj_id, writable, listener);
rewardObjIdCombo = addEnumValueBox(pane, "Skill ID: ", Requirement.SkillID.values(), skillId, writable, listener);
rewardObj = null;
rewardValue = null;
break;
@@ -613,10 +677,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);
}
@@ -633,7 +697,7 @@ public class DialogueEditor extends JSONElementEditor {
removeElementListener(requirementObj);
}
requirementTypeCombo = addEnumValueBox(pane, "Requirement type: ", Requirement.RequirementType.values(), requirement.type, writable, listener);
requirementTypeCombo = addEnumValueBox(pane, "Requirement type: ", Requirement.RequirementType.values(), requirement == null ? null : requirement.type, writable, listener);
requirementParamsPane = new JPanel();
requirementParamsPane.setLayout(new JideBoxLayout(requirementParamsPane, JideBoxLayout.PAGE_AXIS));
updateRequirementParamsEditorPane(requirementParamsPane, requirement, listener);
@@ -679,11 +743,16 @@ 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:
Requirement.SkillID skillId = null;
try {
skillId = requirement.required_obj_id == null ? null : Requirement.SkillID.valueOf(requirement.required_obj_id);
} catch(IllegalArgumentException e) {}
requirementObj = null;
requirementObjId = addTextField(pane, "Skill ID:", requirement.required_obj_id, writable, listener);
requirementSkill = addEnumValueBox(pane, "Skill ID:", Requirement.SkillID.values(), skillId, writable, listener);
requirementObjId = null;//addTextField(pane, "Skill ID:", requirement.required_obj_id, writable, listener);
requirementValue = addIntegerField(pane, "Level: ", requirement.required_value, false, writable, listener);
break;
case timerElapsed:
@@ -691,6 +760,11 @@ public class DialogueEditor extends JSONElementEditor {
requirementObjId = addTextField(pane, "Timer ID:", requirement.required_obj_id, writable, listener);
requirementValue = addIntegerField(pane, "Timer value: ", requirement.required_value, false, writable, listener);
break;
case factionScore:
requirementObj = null;
requirementObjId = addTextField(pane, "Faction ID:", requirement.required_obj_id, writable, listener);
requirementValue = addIntegerField(pane, "Minimum score: ", requirement.required_value, true, writable, listener);
break;
case wear:
requirementObj = addItemBox(pane, project, "Item: ", (Item) requirement.required_obj, writable, listener);
requirementObjId = null;
@@ -796,14 +870,31 @@ public class DialogueEditor extends JSONElementEditor {
label.setIcon(new ImageIcon(DefaultIcons.getObjectLayerIcon()));
break;
case actorCondition:
label.setText("Give actor condition "+rewardObjDesc+" for "+reward.reward_value+" turns");
boolean rewardClear = reward.reward_value != null && reward.reward_value.intValue() == ActorCondition.MAGNITUDE_CLEAR;
if (rewardClear) {
label.setText("Clear actor condition "+rewardObjDesc);
} else {
boolean rewardForever = reward.reward_value != null && reward.reward_value.intValue() == ActorCondition.DURATION_FOREVER;
label.setText("Give actor condition "+rewardObjDesc+(rewardForever ? " forever" : " for "+reward.reward_value+" turns"));
}
if (reward.reward_obj != null) label.setIcon(new ImageIcon(reward.reward_obj.getIcon()));
break;
case actorConditionImmunity:
boolean rewardForever = reward.reward_value == null || reward.reward_value.intValue() == ActorCondition.DURATION_FOREVER;
label.setText("Give immunity to actor condition "+rewardObjDesc+(rewardForever ? " forever" : " for "+reward.reward_value+" turns"));
if (reward.reward_obj != null) label.setIcon(new OverlayIcon(reward.reward_obj.getIcon(), DefaultIcons.getImmunityIcon()));
break;
case alignmentChange:
label.setText("Change alignment for faction "+rewardObjDesc+" : "+reward.reward_value);
label.setIcon(new ImageIcon(DefaultIcons.getAlignmentIcon()));
break;
case alignmentSet:
label.setText("Set alignment for faction "+rewardObjDesc+" : "+reward.reward_value);
label.setIcon(new ImageIcon(DefaultIcons.getAlignmentIcon()));
break;
case createTimer:
label.setText("Create timer "+rewardObjDesc);
label.setIcon(new ImageIcon(DefaultIcons.getTimerIcon()));
break;
case deactivateMapObjectGroup:
label.setText("Deactivate map object group "+rewardObjDesc+" on map "+reward.map_name);
@@ -825,12 +916,17 @@ public class DialogueEditor extends JSONElementEditor {
label.setText("Give quest progress "+rewardObjDesc+":"+reward.reward_value);
if (reward.reward_obj != null) label.setIcon(new ImageIcon(reward.reward_obj.getIcon()));
break;
case removeQuestProgress:
label.setText("Removes quest progress "+rewardObjDesc+":"+reward.reward_value);
if (reward.reward_obj != null) label.setIcon(new ImageIcon(reward.reward_obj.getIcon()));
break;
case removeSpawnArea:
label.setText("Remove all monsters in spawnarea area "+rewardObjDesc+" on map "+reward.map_name);
label.setIcon(new ImageIcon(DefaultIcons.getNPCIcon()));
break;
case skillIncrease:
label.setText("Increase skill "+rewardObjDesc+" level");
label.setIcon(new ImageIcon(DefaultIcons.getSkillIcon()));
break;
case spawnAll:
label.setText("Respawn all monsters in spawnarea area "+rewardObjDesc+" on map "+reward.map_name);
@@ -1065,7 +1161,18 @@ public class DialogueEditor extends JSONElementEditor {
if (req.required_obj.getIcon() != null) {
label.setIcon(new ImageIcon(req.required_obj.getIcon()));
}
} if (req.type == null) {
} else if (req.type == Requirement.RequirementType.skillLevel) {
label.setIcon(new ImageIcon(DefaultIcons.getSkillIcon()));
} else if (req.type == Requirement.RequirementType.spentGold) {
label.setIcon(new ImageIcon(DefaultIcons.getGoldIcon()));
} else if (req.type == Requirement.RequirementType.consumedBonemeals) {
label.setIcon(new ImageIcon(DefaultIcons.getBonemealIcon()));
} else if (req.type == Requirement.RequirementType.timerElapsed) {
label.setIcon(new ImageIcon(DefaultIcons.getTimerIcon()));
} else if (req.type == Requirement.RequirementType.factionScore) {
label.setIcon(new ImageIcon(DefaultIcons.getAlignmentIcon()));
}
if (req.type == null) {
label.setText("New, undefined requirement.");
}
}
@@ -1149,7 +1256,33 @@ 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 == rewardConditionClear) {
selectedReward.reward_value = ActorCondition.MAGNITUDE_CLEAR;
rewardValue.setEnabled(false);
rewardsListModel.itemChanged(selectedReward);
} else if (source == rewardConditionForever) {
selectedReward.reward_value = ActorCondition.DURATION_FOREVER;
rewardValue.setEnabled(false);
rewardsListModel.itemChanged(selectedReward);
} else if (source == rewardConditionTimed) {
selectedReward.reward_value = (Integer) ((JSpinner)rewardValue).getValue();
rewardValue.setEnabled(true);
rewardsListModel.itemChanged(selectedReward);
} else if (source == replyTypeCombo) {
updateRepliesParamsEditorPane(repliesParamsPane, selectedReply, this);
@@ -1185,12 +1318,35 @@ public class DialogueEditor extends JSONElementEditor {
selectedRequirement.required_obj_id = null;
}
requirementsListModel.itemChanged(selectedRequirement);
} else if (source == requirementSkill) {
if (selectedRequirement.required_obj != null) {
selectedRequirement.required_obj.removeBacklink(dialogue);
selectedRequirement.required_obj = null;
}
if (selectedRequirement.type == Requirement.RequirementType.skillLevel) {
selectedRequirement.required_obj_id = value == null ? null : value.toString();
}
requirementsListModel.itemChanged(selectedRequirement);
} else if (source == requirementObjId) {
selectedRequirement.required_obj_id = (String) value;
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;

View File

@@ -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);

View File

@@ -16,6 +16,7 @@ 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;
@@ -29,6 +30,8 @@ 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;
@@ -37,6 +40,8 @@ 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.JideTabbedPane;
@@ -65,7 +70,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);
@@ -85,6 +94,7 @@ public abstract class JSONElementEditor extends Editor {
jsonEditorPane.setText(((JSONElement)target).toJsonString());
jsonEditorPane.setEditable(((JSONElement)target).writable);
jsonEditorPane.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JSON);
jsonEditorPane.setFont(jsonEditorPane.getFont().deriveFont(ATContentStudio.SCALING * jsonEditorPane.getFont().getSize()));
JPanel result = new JPanel();
result.setLayout(new BorderLayout());
result.add(jsonEditorPane, BorderLayout.CENTER);
@@ -178,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);
}
}
}
@@ -283,6 +302,7 @@ public abstract class JSONElementEditor extends Editor {
}
@SuppressWarnings("unchecked")
public boolean idChanging() {
JSONElement node = (JSONElement) target;
List<GameDataElement> toModify = new LinkedList<GameDataElement>();

View File

@@ -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;
@@ -38,20 +40,23 @@ public class QuestEditor extends JSONElementEditor {
private static final long serialVersionUID = 5701667955210615366L;
private static final Integer one = 1;
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,245 +74,217 @@ 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
@@ -337,6 +314,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);
}
@@ -351,47 +340,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;
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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;
@@ -161,6 +162,7 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
private JComboBox evaluateTriggerBox;
private JSpinner quantityField;
private JCheckBox spawnActiveForNewGame;
private JCheckBox spawnIgnoreAreas;
private JTextField spawngroupField;
@SuppressWarnings("rawtypes")
private JList npcList;
@@ -172,7 +174,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")
@@ -205,11 +207,12 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
JScrollPane tmxScroller = new JScrollPane(getTmxEditorPane(), JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
JScrollPane xmlScroller = new JScrollPane(getXmlEditorPane(), JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
JScrollPane replScroller = new JScrollPane(getReplacementSimulatorPane(), JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
//JScrollPane replScroller = new JScrollPane(getReplacementSimulatorPane(), JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
xmlScroller.getVerticalScrollBar().setUnitIncrement(16);
editorTabsHolder.add("TMX", tmxScroller);
editorTabsHolder.add("XML", xmlScroller);
editorTabsHolder.add("Replacements", replScroller);
//editorTabsHolder.add("Replacements", replScroller);
editorTabsHolder.add("Testing", getReplacementSimulatorPane());
}
@@ -259,7 +262,12 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
addTileLayer.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
layerListModel.addObject(new tiled.core.TileLayer());
tiled.core.TileLayer layer = new tiled.core.TileLayer(map.tmxMap.getWidth(), map.tmxMap.getHeight());
layerListModel.addObject(layer);
map.state = GameDataElement.State.modified;
map.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(TMXMapEditor.this);
targetUpdated();
}
});
addObjectGroup = new JButton(new ImageIcon(DefaultIcons.getCreateObjectGroupIcon()));
@@ -269,6 +277,10 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
@Override
public void actionPerformed(ActionEvent e) {
layerListModel.addObject(new tiled.core.ObjectGroup());
map.state = GameDataElement.State.modified;
map.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(TMXMapEditor.this);
targetUpdated();
}
});
deleteLayer = new JButton(new ImageIcon(DefaultIcons.getNullifyIcon()));
@@ -278,6 +290,10 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
@Override
public void actionPerformed(ActionEvent e) {
layerListModel.removeObject(selectedLayer);
map.state = GameDataElement.State.modified;
map.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(TMXMapEditor.this);
targetUpdated();
}
});
JPanel layersButtonsPane = new JPanel();
@@ -362,6 +378,10 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
@Override
public void actionPerformed(ActionEvent e) {
groupObjectsListModel.addObject(MapObject.newMapchange(new tiled.core.MapObject(0, 0, 32, 32), map));
map.state = GameDataElement.State.modified;
map.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(TMXMapEditor.this);
targetUpdated();
}
});
addSpawn = new JButton(new ImageIcon(DefaultIcons.getCreateSpawnareaIcon()));
@@ -371,6 +391,10 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
@Override
public void actionPerformed(ActionEvent e) {
groupObjectsListModel.addObject(MapObject.newSpawnArea(new tiled.core.MapObject(0, 0, 32, 32), map));
map.state = GameDataElement.State.modified;
map.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(TMXMapEditor.this);
targetUpdated();
}
});
addRest = new JButton(new ImageIcon(DefaultIcons.getCreateRestIcon()));
@@ -380,6 +404,10 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
@Override
public void actionPerformed(ActionEvent e) {
groupObjectsListModel.addObject(MapObject.newRest(new tiled.core.MapObject(0, 0, 32, 32), map));
map.state = GameDataElement.State.modified;
map.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(TMXMapEditor.this);
targetUpdated();
}
});
addKey = new JButton(new ImageIcon(DefaultIcons.getCreateKeyIcon()));
@@ -389,6 +417,10 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
@Override
public void actionPerformed(ActionEvent e) {
groupObjectsListModel.addObject(MapObject.newKey(new tiled.core.MapObject(0, 0, 32, 32), map));
map.state = GameDataElement.State.modified;
map.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(TMXMapEditor.this);
targetUpdated();
}
});
addReplace = new JButton(new ImageIcon(DefaultIcons.getCreateReplaceIcon()));
@@ -398,6 +430,10 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
@Override
public void actionPerformed(ActionEvent e) {
groupObjectsListModel.addObject(MapObject.newReplace(new tiled.core.MapObject(0, 0, 32, 32), map));
map.state = GameDataElement.State.modified;
map.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(TMXMapEditor.this);
targetUpdated();
}
});
addScript = new JButton(new ImageIcon(DefaultIcons.getCreateScriptIcon()));
@@ -407,6 +443,10 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
@Override
public void actionPerformed(ActionEvent e) {
groupObjectsListModel.addObject(MapObject.newScript(new tiled.core.MapObject(0, 0, 32, 32), map));
map.state = GameDataElement.State.modified;
map.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(TMXMapEditor.this);
targetUpdated();
}
});
addContainer = new JButton(new ImageIcon(DefaultIcons.getCreateContainerIcon()));
@@ -416,6 +456,10 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
@Override
public void actionPerformed(ActionEvent e) {
groupObjectsListModel.addObject(MapObject.newContainer(new tiled.core.MapObject(0, 0, 32, 32), map));
map.state = GameDataElement.State.modified;
map.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(TMXMapEditor.this);
targetUpdated();
}
});
addSign = new JButton(new ImageIcon(DefaultIcons.getCreateSignIcon()));
@@ -425,6 +469,10 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
@Override
public void actionPerformed(ActionEvent e) {
groupObjectsListModel.addObject(MapObject.newSign(new tiled.core.MapObject(0, 0, 32, 32), map));
map.state = GameDataElement.State.modified;
map.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(TMXMapEditor.this);
targetUpdated();
}
});
deleteObject = new JButton(new ImageIcon(DefaultIcons.getNullifyIcon()));
@@ -434,6 +482,10 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
@Override
public void actionPerformed(ActionEvent e) {
groupObjectsListModel.removeObject(selectedMapObject);
map.state = GameDataElement.State.modified;
map.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(TMXMapEditor.this);
targetUpdated();
}
});
@@ -473,6 +525,7 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
if (selected instanceof ContainerArea) {
droplistBox = addDroplistBox(pane, ((TMXMap)target).getProject(), "Droplist: ", ((ContainerArea)selected).droplist, ((TMXMap)target).writable, listener);
} else if (selected instanceof KeyArea) {
areaField = addTextField(pane, "Area ID: ", ((KeyArea)selected).name, ((TMXMap)target).writable, listener);
dialogueBox = addDialogueBox(pane, ((TMXMap)target).getProject(), "Message when locked: ", ((KeyArea)selected).dialogue, ((TMXMap)target).writable, listener);
requirementTypeCombo = addEnumValueBox(pane, "Requirement type: ", Requirement.RequirementType.values(), ((KeyArea)selected).requirement.type, ((TMXMap)target).writable, listener);
requirementParamsPane = new JPanel();
@@ -511,8 +564,8 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
});
pane.add(tACPane, JideBoxLayout.FIX);
} else if (selected instanceof ReplaceArea) {
//Replace areas only use questProgress requirements ATM
//requirementTypeCombo = addEnumValueBox(pane, "Requirement type: ", Requirement.RequirementType.values(), ((ReplaceArea)selected).requirement.type, ((TMXMap)target).writable, listener);
areaField = addTextField(pane, "Area ID: ", ((ReplaceArea)selected).name, ((TMXMap)target).writable, listener);
requirementTypeCombo = addEnumValueBox(pane, "Requirement type: ", Requirement.RequirementType.values(), ((ReplaceArea)selected).requirement.type, ((TMXMap)target).writable, listener);
requirementParamsPane = new JPanel();
requirementParamsPane.setLayout(new JideBoxLayout(requirementParamsPane, JideBoxLayout.PAGE_AXIS, 6));
pane.add(requirementParamsPane, JideBoxLayout.FIX);
@@ -577,6 +630,7 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
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);
spawnActiveForNewGame = addBooleanBasedCheckBox(pane, "Active in a new game: ", ((SpawnArea)selected).active, ((TMXMap)target).writable, listener);
spawnIgnoreAreas = addBooleanBasedCheckBox(pane, "Monsters can walk on other game objects: ", ((SpawnArea)selected).ignoreAreas, ((TMXMap)target).writable, listener);
npcListModel = new SpawnGroupNpcListModel((SpawnArea) selected);
npcList = new JList(npcListModel);
npcList.setCellRenderer(new GDERenderer(true, ((TMXMap)target).writable));
@@ -642,11 +696,15 @@ 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;
requirementObjId = addTextField(pane, "Skill ID:", requirement.required_obj_id, writable, listener);
Requirement.SkillID skillId = null;
try {
skillId = requirement.required_obj_id == null ? null : Requirement.SkillID.valueOf(requirement.required_obj_id);
} catch(IllegalArgumentException e) {}
requirementObj = addEnumValueBox(pane, "Skill ID:", Requirement.SkillID.values(), skillId, writable, listener);
requirementObjId = null;//addTextField(pane, "Skill ID:", requirement.required_obj_id, writable, listener);
requirementValue = addIntegerField(pane, "Level: ", requirement.required_value, false, writable, listener);
break;
case timerElapsed:
@@ -654,6 +712,11 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
requirementObjId = addTextField(pane, "Timer ID:", requirement.required_obj_id, writable, listener);
requirementValue = addIntegerField(pane, "Timer value: ", requirement.required_value, false, writable, listener);
break;
case factionScore:
requirementObj = null;
requirementObjId = addTextField(pane, "Faction ID:", requirement.required_obj_id, writable, listener);
requirementValue = addIntegerField(pane, "Minimum score: ", requirement.required_value, true, writable, listener);
break;
case wear:
requirementObj = addItemBox(pane, project, "Item: ", (Item) requirement.required_obj, writable, listener);
requirementObjId = null;
@@ -704,6 +767,7 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
editorPane.setText(((TMXMap)target).toXml());
editorPane.setEditable(false);
editorPane.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_XML);
editorPane.setFont(editorPane.getFont().deriveFont(ATContentStudio.SCALING * editorPane.getFont().getSize()));
pane.add(editorPane, JideBoxLayout.VARY);
return pane;
@@ -718,7 +782,11 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
public JPanel getReplacementSimulatorPane() {
JPanel replacementSimulator = new JPanel();
replacementSimulator.setLayout(new JideBoxLayout(replacementSimulator, JideBoxLayout.PAGE_AXIS));
JPanel toolsPane = new JPanel();
toolsPane.setLayout(new JideBoxLayout(toolsPane, JideBoxLayout.LINE_AXIS));
final JCheckBox walkableVisibleBox = new JCheckBox("Show \""+TMXMap.WALKABLE_LAYER_NAME+"\" layer.");
final JCheckBox showHeroBox = new JCheckBox("Show hero on walkable tiles under the mouse pointer.");
final JCheckBox showTooltipBox = new JCheckBox("Show tooltip with stack of tiles.");
JPanel areasActivationPane = new JPanel();
areasActivationPane.setLayout(new JideBoxLayout(areasActivationPane, JideBoxLayout.PAGE_AXIS));
TreeModel areasTreeModel = new ReplaceAreasActivationTreeModel();
@@ -734,12 +802,17 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
activateAndViewPane.setLayout(new JideBoxLayout(activateAndViewPane, JideBoxLayout.LINE_AXIS));
activateAndViewPane.add(areasActivationPane, JideBoxLayout.FIX);
activateAndViewPane.add(viewer, JideBoxLayout.VARY);
activateAndViewPane.add(new JScrollPane(viewer), JideBoxLayout.VARY);
replacementSimulator.add(walkableVisibleBox, JideBoxLayout.FIX);
toolsPane.add(walkableVisibleBox, JideBoxLayout.FIX);
toolsPane.add(showTooltipBox, JideBoxLayout.FIX);
toolsPane.add(showHeroBox, JideBoxLayout.FIX);
toolsPane.add(new JPanel(), JideBoxLayout.VARY);
replacementSimulator.add(toolsPane, JideBoxLayout.FIX);
replacementSimulator.add(activateAndViewPane, JideBoxLayout.VARY);
walkableVisibleBox.setSelected(true);
walkableVisibleBox.setSelected(viewer.showWalkable);
walkableVisibleBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
@@ -748,6 +821,21 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
viewer.repaint();
}
});
showHeroBox.setSelected(viewer.showHeroWithMouse);
showHeroBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
viewer.showHeroWithMouse = showHeroBox.isSelected();
}
});
showTooltipBox.setSelected(viewer.showTooltip);
showTooltipBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
viewer.showTooltip = showTooltipBox.isSelected();
}
});
activate.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
@@ -1592,6 +1680,9 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
public void targetUpdated() {
this.name = ((TMXMap)target).getDesc();
updateMessage();
updateXmlViewText(((TMXMap)target).toXml());
tmxViewer.repaint();
tmxViewer.revalidate();
}
@@ -1681,6 +1772,10 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
ATContentStudio.frame.closeEditor(map);
map.childrenRemoved(new ArrayList<ProjectTreeNode>());
map.delete();
GameDataElement newOne = map.getProject().getMap(map.id);
for (GameDataElement backlink : map.getBacklinks()) {
backlink.elementChanged(map, newOne);
}
}
});
savePane.add(delete, JideBoxLayout.FIX);
@@ -1754,6 +1849,7 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
}
private int skipAreaFieldEvents = 0;
public class MapFieldUpdater implements FieldUpdateListener {
@SuppressWarnings({ "unchecked", "rawtypes" })
@@ -1789,8 +1885,22 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
tmxViewer.revalidate();
tmxViewer.repaint();
} else if (source == areaField) {
selectedMapObject.name = (String) value;
groupObjectsListModel.objectChanged(selectedMapObject);
if (skipAreaFieldEvents > 0) skipAreaFieldEvents--;
else {
selectedMapObject.name = (String) value;
if (selectedMapObject instanceof KeyArea) {
KeyArea area = (KeyArea) selectedMapObject;
if (area.oldSchoolRequirement) {
area.oldSchoolRequirement = false;
}
} else if (selectedMapObject instanceof ReplaceArea) {
ReplaceArea area = (ReplaceArea) selectedMapObject;
if (area.oldSchoolRequirement) {
area.oldSchoolRequirement = false;
}
}
groupObjectsListModel.objectChanged(selectedMapObject);
}
} else if (source == spawngroupField) {
if (selectedMapObject instanceof SpawnArea) {
SpawnArea area = (SpawnArea)selectedMapObject;
@@ -1909,62 +2019,148 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
SpawnArea area = (SpawnArea) selectedMapObject;
area.active = (Boolean) value;
}
} else if (source == spawnIgnoreAreas) {
if (selectedMapObject instanceof SpawnArea) {
SpawnArea area = (SpawnArea) selectedMapObject;
area.ignoreAreas = (Boolean) value;
}
} else if (source == requirementTypeCombo) {
if (selectedMapObject instanceof KeyArea) {
KeyArea area = (KeyArea) selectedMapObject;
area.requirement.changeType((Requirement.RequirementType)requirementTypeCombo.getSelectedItem());
updateRequirementParamsPane(requirementParamsPane, area.requirement, this);
if (area.oldSchoolRequirement) area.updateNameFromRequirementChange();
if (area.oldSchoolRequirement) {
area.updateNameFromRequirementChange();
skipAreaFieldEvents+=2;
areaField.setText(area.name);
}
} else if (selectedMapObject instanceof ReplaceArea) {
ReplaceArea area = (ReplaceArea) selectedMapObject;
area.requirement.changeType((Requirement.RequirementType)requirementTypeCombo.getSelectedItem());
updateRequirementParamsPane(requirementParamsPane, area.requirement, this);
if (area.oldSchoolRequirement) {
area.updateNameFromRequirementChange();
skipAreaFieldEvents+=2;
areaField.setText(area.name);
}
}
} else if (source == requirementObj) {
if (selectedMapObject instanceof KeyArea) {
KeyArea area = (KeyArea) selectedMapObject;
area.requirement.required_obj = (GameDataElement) value;
if (area.requirement.required_obj != null) {
area.requirement.required_obj_id = area.requirement.required_obj.id;
if (area.requirement.type == Requirement.RequirementType.skillLevel) {
area.requirement.required_obj_id = value == null ? null : value.toString();
} else {
area.requirement.required_obj_id = null;
area.requirement.required_obj = (GameDataElement) value;
if (area.requirement.required_obj != null) {
area.requirement.required_obj_id = area.requirement.required_obj.id;
} else {
area.requirement.required_obj_id = null;
}
}
if (area.oldSchoolRequirement) {
area.updateNameFromRequirementChange();
skipAreaFieldEvents+=2;
areaField.setText(area.name);
}
if (area.oldSchoolRequirement) area.updateNameFromRequirementChange();
} else if (selectedMapObject instanceof ReplaceArea) {
ReplaceArea area = (ReplaceArea) selectedMapObject;
area.requirement.required_obj = (GameDataElement) value;
if (area.requirement.required_obj != null) {
area.requirement.required_obj_id = area.requirement.required_obj.id;
if (area.requirement.type == Requirement.RequirementType.skillLevel) {
area.requirement.required_obj_id = value == null ? null : value.toString();
} else {
area.requirement.required_obj_id = null;
area.requirement.required_obj = (GameDataElement) value;
if (area.requirement.required_obj != null) {
area.requirement.required_obj_id = area.requirement.required_obj.id;
} else {
area.requirement.required_obj_id = null;
}
}
if (area.oldSchoolRequirement) {
area.updateNameFromRequirementChange();
skipAreaFieldEvents+=2;
areaField.setText(area.name);
}
if (area.oldSchoolRequirement) area.updateNameFromRequirementChange();
}
} else if (source == requirementObjId) {
if (selectedMapObject instanceof KeyArea) {
KeyArea area = (KeyArea) selectedMapObject;
area.requirement.required_obj_id = (String) value;
area.requirement.required_obj = null;
if (area.oldSchoolRequirement) {
area.updateNameFromRequirementChange();
skipAreaFieldEvents+=2;
areaField.setText(area.name);
}
} else if (selectedMapObject instanceof ReplaceArea) {
ReplaceArea area = (ReplaceArea) selectedMapObject;
area.requirement.required_obj_id = (String) value;
area.requirement.required_obj = null;
if (area.oldSchoolRequirement) {
area.updateNameFromRequirementChange();
skipAreaFieldEvents+=2;
areaField.setText(area.name);
}
}
} 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 (area.oldSchoolRequirement) area.updateNameFromRequirementChange();
if (quest != null) {
stage = quest.getStage(area.requirement.required_value);
if (stage != null) stage.addBacklink(map);
}
if (area.oldSchoolRequirement) {
area.updateNameFromRequirementChange();
skipAreaFieldEvents+=2;
areaField.setText(area.name);
}
} 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 (area.oldSchoolRequirement) area.updateNameFromRequirementChange();
if (quest != null) {
stage = quest.getStage(area.requirement.required_value);
if (stage != null) stage.addBacklink(map);
}
if (area.oldSchoolRequirement) {
area.updateNameFromRequirementChange();
skipAreaFieldEvents+=2;
areaField.setText(area.name);
}
}
} else if (source == requirementNegated) {
if (selectedMapObject instanceof KeyArea) {
KeyArea area = (KeyArea) selectedMapObject;
area.requirement.negated = (Boolean) value;
if (area.oldSchoolRequirement) area.updateNameFromRequirementChange();
if (area.oldSchoolRequirement) {
area.updateNameFromRequirementChange();
skipAreaFieldEvents+=2;
areaField.setText(area.name);
}
} else if (selectedMapObject instanceof ReplaceArea) {
ReplaceArea area = (ReplaceArea) selectedMapObject;
area.requirement.negated = (Boolean) value;
if (area.oldSchoolRequirement) area.updateNameFromRequirementChange();
if (area.oldSchoolRequirement) {
area.updateNameFromRequirementChange();
skipAreaFieldEvents+=2;
areaField.setText(area.name);
}
}
} else if (source == sourceLayer) {
selectedReplacement.sourceLayer = (String)value;
@@ -1978,11 +2174,9 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
if (modified) {
if (map.state != GameDataElement.State.modified) {
map.state = GameDataElement.State.modified;
TMXMapEditor.this.name = map.getDesc();
map.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(TMXMapEditor.this);
}
updateXmlViewText(map.toXml());
map.childrenChanged(new ArrayList<ProjectTreeNode>());
}
}
}
@@ -2013,11 +2207,13 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
private TMXMap map;
public ReplaceArea highlighted = null;
public boolean showWalkable = true;
public boolean showHeroWithMouse = true;
public boolean showTooltip = true;
private OrthogonalRenderer renderer;
private String groundName, objectsName, aboveName, walkableName;
private String groundName, objectsName, aboveName, topName, walkableName;
private Map<String, tiled.core.TileLayer> layersByName = new LinkedHashMap<String, tiled.core.TileLayer>();
private tiled.core.TileLayer ground, objects, above, walkable;
private tiled.core.TileLayer ground, objects, above, top, walkable;
private Map<String, List<ReplaceArea>> replacementsForLayer = new LinkedHashMap<String, List<ReplaceArea>>();
public Map<ReplaceArea, Boolean> activeReplacements = new LinkedHashMap<ReplaceArea, Boolean>();
@@ -2036,8 +2232,9 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
Point oldTooltippedTile = new Point(tooltippedTile);
tooltippedTile.setLocation(e.getX() / 32, e.getY() / 32);
if (!((TMXMap)target).tmxMap.contains(tooltippedTile.x, tooltippedTile.y)) {
if (!showTooltip || !((TMXMap)target).tmxMap.contains(tooltippedTile.x, tooltippedTile.y)) {
if (tooltipActivated) {
//Hides the tooltip...
ToolTipManager.sharedInstance().setEnabled(false);
@@ -2045,12 +2242,16 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
tooltipActivated = false;
}
} else {
if (!tooltipActivated) {
if (showTooltip && !tooltipActivated) {
ToolTipManager.sharedInstance().registerComponent(TMXReplacementViewer.this);
ToolTipManager.sharedInstance().setEnabled(true);
tooltipActivated = true;
}
}
if (showHeroWithMouse && (oldTooltippedTile.x != tooltippedTile.x || oldTooltippedTile.y != tooltippedTile.y) ) {
TMXReplacementViewer.this.revalidate();
TMXReplacementViewer.this.repaint();
}
}
});
@@ -2060,9 +2261,9 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
}
public void init() {
groundName = objectsName = aboveName = walkableName = null;
groundName = objectsName = aboveName = walkableName = topName = null;
layersByName.clear();
ground = objects = above = walkable = null;
ground = objects = above = walkable = top = null;
replacementsForLayer.clear();
for (tiled.core.MapLayer layer : map.tmxMap.getLayers()) {
@@ -2074,6 +2275,8 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
objectsName = layer.getName();
} else if (TMXMap.ABOVE_LAYER_NAME.equalsIgnoreCase(layer.getName())) {
aboveName = layer.getName();
} else if (TMXMap.TOP_LAYER_NAME.equalsIgnoreCase(layer.getName())) {
topName = layer.getName();
} else if (TMXMap.WALKABLE_LAYER_NAME.equalsIgnoreCase(layer.getName())) {
walkableName = layer.getName();
}
@@ -2103,6 +2306,7 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
ground = mergeReplacements(groundName);
objects = mergeReplacements(objectsName);
above = mergeReplacements(aboveName);
top = mergeReplacements(topName);
walkable = mergeReplacements(walkableName);
}
@@ -2158,10 +2362,18 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
renderer.paintTileLayer(g2d, objects);
}
if (showHeroWithMouse && tooltippedTile != null && ((TMXMap)target).tmxMap.contains(tooltippedTile.x, tooltippedTile.y) &&
walkable != null && walkable.getTileAt(tooltippedTile.x, tooltippedTile.y) == null) {
g2d.drawImage(DefaultIcons.getHeroImage(), tooltippedTile.x * 32, tooltippedTile.y * 32, 32, 32, null);
}
if (above != null) {
renderer.paintTileLayer(g2d, above);
}
if (top != null) {
renderer.paintTileLayer(g2d, top);
}
if (walkable != null && showWalkable) {
renderer.paintTileLayer(g2d, walkable);
}
@@ -2218,10 +2430,12 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
JLabel noTileGround = new JLabel(new ImageIcon(DefaultIcons.getNullifyImage().getScaledInstance(32, 32, Image.SCALE_DEFAULT)));
JLabel noTileObjects = new JLabel(new ImageIcon(DefaultIcons.getNullifyImage().getScaledInstance(32, 32, Image.SCALE_DEFAULT)));
JLabel noTileAbove = new JLabel(new ImageIcon(DefaultIcons.getNullifyImage().getScaledInstance(32, 32, Image.SCALE_DEFAULT)));
JLabel noTileTop = new JLabel(new ImageIcon(DefaultIcons.getNullifyImage().getScaledInstance(32, 32, Image.SCALE_DEFAULT)));
{
noTileGround.setPreferredSize(new Dimension(32, 32));
noTileObjects.setPreferredSize(new Dimension(32, 32));
noTileAbove.setPreferredSize(new Dimension(32, 32));
noTileTop.setPreferredSize(new Dimension(32, 32));
}
Point tooltippedTile = new Point();
JToolTip tt = null;
@@ -2243,6 +2457,20 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
if (tooltippedTile != null) {
Image tile;
JLabel label;
if (top != null && top.getTileAt(tooltippedTile.x, tooltippedTile.y) != null) {
tile = top.getTileAt(tooltippedTile.x, tooltippedTile.y).getImage();
} else {
tile = null;
}
if (tile != null) {
label = new JLabel(new ImageIcon(tile));
label.setPreferredSize(new Dimension(32,32));
content.add(label, JideBoxLayout.FIX);
//Fix when (if?) Top is advertised publicly.
// } else {
// content.add(noTileTop, JideBoxLayout.FIX);
}
if (above != null && above.getTileAt(tooltippedTile.x, tooltippedTile.y) != null) {
tile = above.getTileAt(tooltippedTile.x, tooltippedTile.y).getImage();

View File

@@ -1,33 +1,47 @@
package com.gpl.rpg.atcontentstudio.ui.map;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.ButtonGroup;
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.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.JViewport;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
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.GameSource;
import com.gpl.rpg.atcontentstudio.model.ProjectTreeNode;
@@ -35,16 +49,16 @@ import com.gpl.rpg.atcontentstudio.model.SaveEvent;
import com.gpl.rpg.atcontentstudio.model.maps.TMXMap;
import com.gpl.rpg.atcontentstudio.model.maps.Worldmap;
import com.gpl.rpg.atcontentstudio.model.maps.WorldmapSegment;
import com.gpl.rpg.atcontentstudio.model.maps.WorldmapSegment.NamedArea;
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.SaveItemsWizard;
import com.gpl.rpg.atcontentstudio.ui.WorldmapLabelEditionWizard;
import com.jidesoft.swing.ComboBoxSearchable;
import com.jidesoft.swing.JideBoxLayout;
import com.jidesoft.swing.JideTabbedPane;
import com.jidesoft.swing.ListSearchable;
public class WorldMapEditor extends Editor {
public class WorldMapEditor extends Editor implements FieldUpdateListener {
private static final long serialVersionUID = -8358238912588729094L;
@@ -56,18 +70,35 @@ public class WorldMapEditor extends Editor {
public enum EditMode {
moveViewSelect,
moveMaps,
deleteMaps,
addMap,
editLabelCoverage
addMap
}
public String mapBeingAddedID = null;
public String selectedLabel = null;
WorldMapView mapView = null;
WorldmapSegment.NamedArea selectedLabel = null;
MapSegmentMapsListModel msmListModel = null;
ListSelectionModel msmListSelectionModel = null;
MapSegmentLabelsListModel mslListModel = null;
MapSegmentLabelMapsListModel mslmListModel = null;
ListSelectionModel mslmListSelectionModel = null;
ListModel<TMXMap> currentSelectionListModel = null;
ListSelectionModel currentSelectionSelectionModel = null;
ListModel<TMXMap> currentHighlightListModel = null;
JList<TMXMap> mapsShown;
JList<WorldmapSegment.NamedArea> labelList;
JTextField labelIdField;
JTextField labelNameField;
JTextField labelTypeField;
public WorldMapEditor(WorldmapSegment worldmap) {
target = worldmap;
this.name = worldmap.id;
this.name = worldmap.getDesc();
this.icon = new ImageIcon(worldmap.getIcon());
setLayout(new BorderLayout());
@@ -86,8 +117,10 @@ public class WorldMapEditor extends Editor {
@Override
public void targetUpdated() {
// TODO Auto-generated method stub
this.name = ((GameDataElement)target).getDesc();
updateMessage();
updateXmlViewText(((WorldmapSegment)target).toXml());
mapView.updateFromModel();
}
public JPanel getXmlEditorPane() {
@@ -97,6 +130,7 @@ public class WorldMapEditor extends Editor {
editorPane.setText(((WorldmapSegment)target).toXml());
editorPane.setEditable(false);
editorPane.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_XML);
editorPane.setFont(editorPane.getFont().deriveFont(ATContentStudio.SCALING * editorPane.getFont().getSize()));
pane.add(editorPane, JideBoxLayout.VARY);
return pane;
@@ -128,10 +162,6 @@ public class WorldMapEditor extends Editor {
zoomSliderPane.add(zoomSlider, JideBoxLayout.VARY);
zoomSliderPane.add(new JLabel(new ImageIcon(DefaultIcons.getZoomIcon())), JideBoxLayout.FIX);
final JRadioButton editLabelCoverage = new JRadioButton("Edit label coverage");
final JButton editLabel = new JButton("Edit map label");
final JButton createLabel = new JButton("Create map label");
final JButton deleteLabel = new JButton("Delete map label");
if (target.writable) {
JPanel mapToolsPane = new JPanel();
@@ -143,9 +173,6 @@ public class WorldMapEditor extends Editor {
JRadioButton moveMaps = new JRadioButton("Move selected map(s)");
mapToolsGroup.add(moveMaps);
mapToolsPane.add(moveMaps, JideBoxLayout.FIX);
JRadioButton deleteMaps = new JRadioButton("Delete maps");
mapToolsGroup.add(deleteMaps);
mapToolsPane.add(deleteMaps, JideBoxLayout.FIX);
JRadioButton addMap = new JRadioButton("Add map");
mapToolsGroup.add(addMap);
mapToolsPane.add(addMap, JideBoxLayout.FIX);
@@ -176,22 +203,6 @@ public class WorldMapEditor extends Editor {
moveView.setSelected(true);
pane.add(mapToolsPane, JideBoxLayout.FIX);
JPanel labelToolsPane = new JPanel();
labelToolsPane.setLayout(new JideBoxLayout(labelToolsPane, JideBoxLayout.LINE_AXIS));
mapToolsGroup.add(editLabelCoverage);
editLabelCoverage.setEnabled(false);
labelToolsPane.add(editLabelCoverage, JideBoxLayout.FIX);
editLabel.setEnabled(false);
labelToolsPane.add(editLabel, JideBoxLayout.FIX);
deleteLabel.setEnabled(false);
labelToolsPane.add(deleteLabel, JideBoxLayout.FIX);
createLabel.setEnabled(false);
labelToolsPane.add(createLabel, JideBoxLayout.FIX);
labelToolsPane.add(new JPanel(), JideBoxLayout.VARY);
pane.add(labelToolsPane, JideBoxLayout.FIX);
moveView.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
@@ -220,20 +231,6 @@ public class WorldMapEditor extends Editor {
}
});
deleteMaps.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
editMode = EditMode.deleteMaps;
mapBox.setEnabled(false);
if (mapBeingAddedID != null) {
mapView.mapLocations.remove(mapBeingAddedID);
mapBeingAddedID = null;
mapView.revalidate();
mapView.repaint();
}
}
});
addMap.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
@@ -268,109 +265,27 @@ public class WorldMapEditor extends Editor {
}
});
editLabelCoverage.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
editMode = EditMode.editLabelCoverage;
mapBox.setEnabled(false);
mapView.selected.clear();
mapView.selected.addAll(((WorldmapSegment)target).labelledMaps.get(selectedLabel));
if (mapBeingAddedID != null) {
mapView.mapLocations.remove(mapBeingAddedID);
mapBeingAddedID = null;
}
mapView.revalidate();
mapView.repaint();
}
});
editLabelCoverage.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.DESELECTED) {
WorldmapSegment map = (WorldmapSegment)target;
if (map.labelledMaps.get(selectedLabel) != null) {
map.labelledMaps.get(selectedLabel).clear();
} else {
map.labelledMaps.put(selectedLabel, new ArrayList<String>());
}
for (String s : mapView.selected) {
map.labelledMaps.get(selectedLabel).add(s);
}
notifyModelModified();
mapView.revalidate();
mapView.repaint();
}
}
});
editLabel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
mapView.selected.clear();
mapView.selected.addAll(((WorldmapSegment)target).labelledMaps.get(selectedLabel));
mapView.revalidate();
mapView.repaint();
WorldmapLabelEditionWizard wiz = new WorldmapLabelEditionWizard(worldmap, worldmap.labels.get(selectedLabel));
wiz.addCreationListener(new WorldmapLabelEditionWizard.CreationCompletedListener() {
@Override
public void labelCreated(NamedArea created) {
if (!created.id.equals(selectedLabel)) {
worldmap.labelledMaps.put(created.id, worldmap.labelledMaps.get(selectedLabel));
worldmap.labelledMaps.remove(selectedLabel);
worldmap.labels.put(created.id, created);
worldmap.labels.remove(selectedLabel);
selectedLabel = created.id;
notifyModelModified();
mapView.revalidate();
mapView.repaint();
}
}
});
wiz.setVisible(true);
}
});
deleteLabel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
worldmap.labelledMaps.remove(selectedLabel);
worldmap.labels.remove(selectedLabel);
selectedLabel = null;
notifyModelModified();
mapView.revalidate();
mapView.repaint();
}
});
createLabel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
WorldmapLabelEditionWizard wiz = new WorldmapLabelEditionWizard(worldmap);
wiz.addCreationListener(new WorldmapLabelEditionWizard.CreationCompletedListener() {
@Override
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();
}
});
wiz.setVisible(true);
}
});
}
JPanel mapZoomPane = new JPanel();
mapZoomPane.setLayout(new BorderLayout());
mapZoomPane.add(zoomSliderPane, BorderLayout.WEST);
mapZoomPane.add(mapScroller, BorderLayout.CENTER);
pane.add(mapZoomPane, JideBoxLayout.VARY);
JPanel mapPropsPane = new JPanel();
buildMapPropsPane(mapPropsPane, worldmap);
setCurrentSelectionModel(msmListModel, msmListSelectionModel);
final JSplitPane mapAndPropsSplitter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, mapZoomPane, mapPropsPane);
SwingUtilities.invokeLater(new Runnable(){
@Override
public void run() {
mapAndPropsSplitter.setDividerLocation(0.8d);
}
});
pane.add(mapAndPropsSplitter, JideBoxLayout.VARY);
zoomSlider.addChangeListener(new ChangeListener() {
@Override
@@ -401,7 +316,7 @@ public class WorldMapEditor extends Editor {
@Override
public void mouseClicked(MouseEvent e) {
String selectedMap = null;
boolean update = false;
// boolean update = false;
int x = (int) (e.getX() / mapView.zoomLevel);
int y = (int) (e.getY() / mapView.zoomLevel);
for (String s : mapView.mapLocations.keySet()) {
@@ -410,52 +325,41 @@ public class WorldMapEditor extends Editor {
break;
}
}
if (editMode == EditMode.moveViewSelect || editMode == EditMode.editLabelCoverage) {
if (editMode == EditMode.moveViewSelect) {
if (selectedMap == null) return;
if (e.getButton() == MouseEvent.BUTTON1) {
if (e.isControlDown() || e.isShiftDown()) {
if (mapView.selected.contains(selectedMap)) {
if (editMode != EditMode.editLabelCoverage || mapView.selected.size() > 1) {
mapView.selected.remove(selectedMap);
mapSelectionChanged();
update = true;
if (mapView.getSelectedMapsIDs().contains(selectedMap)) {
if (mapView.getSelectedMapsIDs().size() > 1) {
removeFromSelection(selectedMap);
// mapView.selected.remove(selectedMap);
// update = true;
}
} else {
mapView.selected.add(selectedMap);
mapSelectionChanged();
update = true;
addToSelection(selectedMap);
// mapView.selected.add(selectedMap);
// update = true;
}
} else {
mapView.selected.clear();
mapView.selected.add(selectedMap);
mapSelectionChanged();
update = true;
}
if (e.getClickCount() == 2) {
ATContentStudio.frame.openEditor(worldmap.getProject().getMap(selectedMap));
clearSelection();
// mapView.selected.clear();
addToSelection(selectedMap);
// mapView.selected.add(selectedMap);
// update = true;
}
}
} else if (editMode == EditMode.deleteMaps) {
worldmap.mapLocations.remove(selectedMap);
worldmap.labels.remove(selectedMap);
mapView.selected.remove(selectedMap);
mapSelectionChanged();
mapView.updateFromModel();
notifyModelModified();
update = true;
} else if (editMode == EditMode.addMap && mapBeingAddedID != null) {
if (e.getButton() == MouseEvent.BUTTON1) {
mapView.recomputeSize();
pushToModel();
}
mapView.updateFromModel();
update = true;
// update = true;
mapBeingAddedID = null;
}
if (update) {
mapView.revalidate();
mapView.repaint();
}
// if (update) {
// validateSelection();
// }
}
@Override
@@ -515,7 +419,7 @@ public class WorldMapEditor extends Editor {
mapDeltaX -= mapDeltaX % WorldMapView.TILE_SIZE;
mapDeltaY -= mapDeltaY % WorldMapView.TILE_SIZE;
for (String s : mapView.selected) {
for (String s : mapView.getSelectedMapsIDs()) {
mapView.mapLocations.get(s).x = (worldmap.mapLocations.get(s).x * WorldMapView.TILE_SIZE) + mapDeltaX;
mapView.mapLocations.get(s).y = (worldmap.mapLocations.get(s).y * WorldMapView.TILE_SIZE) + mapDeltaY;
}
@@ -531,54 +435,441 @@ public class WorldMapEditor extends Editor {
}
}
public void mapSelectionChanged() {
if (mapView.selected.isEmpty()) {
editLabelCoverage.setEnabled(false);
editLabel.setEnabled(false);
createLabel.setEnabled(false);
selectedLabel = null;
} else {
String label = null;
boolean multiLabel = false;
for (String map : mapView.selected) {
for (String existingLabel : ((WorldmapSegment)target).labelledMaps.keySet()) {
if (((WorldmapSegment)target).labelledMaps.get(existingLabel).contains(map)) {
if (label != null && !label.equals(existingLabel)) {
multiLabel = true;
}
label = existingLabel;
}
}
}
if (multiLabel) {
editLabelCoverage.setEnabled(false);
editLabel.setEnabled(false);
createLabel.setEnabled(false);
deleteLabel.setEnabled(false);
selectedLabel = null;
} else if (label != null) {
editLabelCoverage.setEnabled(true);
editLabel.setEnabled(true);
deleteLabel.setEnabled(true);
createLabel.setEnabled(false);
selectedLabel = label;
} else {
editLabelCoverage.setEnabled(false);
editLabel.setEnabled(false);
deleteLabel.setEnabled(false);
createLabel.setEnabled(true);
selectedLabel = null;
}
}
}
};
mapView.addMouseListener(mouseListener);
mapView.addMouseMotionListener(mouseListener);
mapView.addMapClickListener(new WorldMapView.MapClickListener() {
@Override
public void mapClicked(MouseEvent e, TMXMap m) {
if (e.getClickCount() == 2) {
ATContentStudio.frame.openEditor(m);
}
}
@Override
public void mapChangeClicked(MouseEvent e, TMXMap m, TMXMap changeTarget) {
if (e.getClickCount() == 2) {
ATContentStudio.frame.openEditor(changeTarget);
}
}
@Override
public void backgroundClicked(MouseEvent e) {
}
});
return pane;
}
private void buildMapPropsPane(JPanel mapPropsPane, final WorldmapSegment worldmap) {
JideTabbedPane tabPane = new JideTabbedPane(JideTabbedPane.TOP);
JPanel mapListPane = new JPanel();
mapListPane.setLayout(new JideBoxLayout(mapListPane, JideBoxLayout.PAGE_AXIS));
mapListPane.add(new JLabel("Maps shown here"), JideBoxLayout.FIX);
msmListModel = new MapSegmentMapsListModel(worldmap);
mapsShown = new JList<TMXMap>(msmListModel);
mapsShown.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
msmListSelectionModel = mapsShown.getSelectionModel();
mapsShown.setCellRenderer(new MapCellRenderer());
new ListSearchable(mapsShown) {
@Override
protected String convertElementToString(Object object) {
return ((TMXMap)object).id;
}
};
mapListPane.add(new JScrollPane(mapsShown), JideBoxLayout.VARY);
mapsShown.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) {
ATContentStudio.frame.openEditor(mapsShown.getSelectedValue());
ATContentStudio.frame.selectInTree(mapsShown.getSelectedValue());
}
}
});
mapsShown.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
ATContentStudio.frame.openEditor(mapsShown.getSelectedValue());
ATContentStudio.frame.selectInTree(mapsShown.getSelectedValue());
}
}
});
mapsShown.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
setCurrentSelectionModel(msmListModel, msmListSelectionModel);
}
});
tabPane.addTab("Map list", mapListPane);
final JPanel labelEditPane = new JPanel();
labelEditPane.setLayout(new JideBoxLayout(labelEditPane, JideBoxLayout.PAGE_AXIS));
labelEditPane.add(new JLabel("Labels on the worldmap"), JideBoxLayout.FIX);
mslListModel = new MapSegmentLabelsListModel(worldmap);
labelList = new JList<WorldmapSegment.NamedArea>(mslListModel);
labelList.setCellRenderer(new MapLabelCellRenderer());
labelList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
labelEditPane.add(new JScrollPane(labelList), JideBoxLayout.FLEXIBLE);
JPanel labelListButtonsPane = new JPanel();
labelListButtonsPane.setLayout(new JideBoxLayout(labelListButtonsPane, JideBoxLayout.LINE_AXIS));
final JButton createLabel = new JButton(new ImageIcon(DefaultIcons.getCreateIcon()));
labelListButtonsPane.add(createLabel, JideBoxLayout.FIX);
final JButton deleteLabel = new JButton(new ImageIcon(DefaultIcons.getNullifyIcon()));
labelListButtonsPane.add(deleteLabel, JideBoxLayout.FIX);
labelListButtonsPane.add(new JPanel(), JideBoxLayout.VARY);
labelEditPane.add(labelListButtonsPane, JideBoxLayout.FIX);
final JPanel labelParametersPane = new JPanel();
labelEditPane.add(labelParametersPane, JideBoxLayout.FLEXIBLE);
labelList.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
selectedLabel = labelList.getSelectedValue();
updateLabelParamsPane(labelParametersPane, worldmap);
labelEditPane.revalidate();
labelEditPane.repaint();
}
});
createLabel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
WorldmapSegment.NamedArea creation = new WorldmapSegment.NamedArea(null, null, null);
worldmap.labels.put(WorldmapSegment.TEMP_LABEL_KEY, creation);
worldmap.labelledMaps.put(WorldmapSegment.TEMP_LABEL_KEY, new ArrayList<String>());
mslListModel.listChanged();
labelList.setSelectedValue(creation, true);
}
});
deleteLabel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (selectedLabel.id != null) {
worldmap.labelledMaps.remove(selectedLabel.id);
worldmap.labels.remove(selectedLabel.id);
} else {
worldmap.labelledMaps.remove(WorldmapSegment.TEMP_LABEL_KEY);
worldmap.labels.remove(WorldmapSegment.TEMP_LABEL_KEY);
}
labelList.clearSelection();
mslListModel.listChanged();
notifyModelModified();
}
});
tabPane.addTab("Labels", labelEditPane);
mapPropsPane.setLayout(new BorderLayout());
mapPropsPane.add(tabPane, BorderLayout.CENTER);
}
private void updateLabelParamsPane(JPanel labelParametersPane, final WorldmapSegment worldmap) {
labelParametersPane.removeAll();
if (selectedLabel == null) {
setCurrentHighlightModel(null);
return;
}
labelParametersPane.setLayout(new JideBoxLayout(labelParametersPane, JideBoxLayout.PAGE_AXIS));
labelIdField = addTextField(labelParametersPane, "Internal ID: ", selectedLabel.id, worldmap.writable, this);
labelNameField = addTranslatableTextField(labelParametersPane, "Name: ", selectedLabel.name, worldmap.writable, this);
labelTypeField = addTextField(labelParametersPane, "Type: ", selectedLabel.type, worldmap.writable, this);
labelParametersPane.add(new JLabel("Label covers the following maps"), JideBoxLayout.FIX);
mslmListModel = new MapSegmentLabelMapsListModel(worldmap, selectedLabel);
final JList<TMXMap> labelCoverageList = new JList<TMXMap>(mslmListModel);
labelCoverageList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
mslmListSelectionModel = labelCoverageList.getSelectionModel();
labelCoverageList.setCellRenderer(new MapCellRenderer());
labelParametersPane.add(new JScrollPane(labelCoverageList), JideBoxLayout.VARY);
labelCoverageList.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
setCurrentHighlightModel(mslmListModel);
}
});
JPanel labelCoverageButtonsPane = new JPanel();
labelCoverageButtonsPane.setLayout(new JideBoxLayout(labelCoverageButtonsPane, JideBoxLayout.LINE_AXIS));
JButton addCoverage = new JButton("Add on-map selection");
labelCoverageButtonsPane.add(addCoverage, JideBoxLayout.FIX);
JButton replaceCoverage = new JButton("Replace by on-map selection");
labelCoverageButtonsPane.add(replaceCoverage, JideBoxLayout.FIX);
JButton removeFromCoverage = new JButton("Remove selected in list");
labelCoverageButtonsPane.add(removeFromCoverage, JideBoxLayout.FIX);
labelCoverageButtonsPane.add(new JPanel(), JideBoxLayout.VARY);
addCoverage.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (selectedLabel == null) return;
String labelId = selectedLabel.id;
if (labelId == null) labelId = WorldmapSegment.TEMP_LABEL_KEY;
List<String> currentCoverage = worldmap.labelledMaps.get(labelId);
if (currentCoverage == null) {
worldmap.labelledMaps.put(labelId, new ArrayList<String>());
currentCoverage = worldmap.labelledMaps.get(labelId);
}
for (int i = 0; i < msmListModel.getSize(); i++) {
if (msmListSelectionModel.isSelectedIndex(i)) {
if (!currentCoverage.contains(msmListModel.getElementAt(i).id)) {
currentCoverage.add(msmListModel.getElementAt(i).id);
}
}
}
mslmListModel.listChanged();
repaintMap();
}
});
replaceCoverage.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (selectedLabel == null) return;
String labelId = selectedLabel.id;
if (labelId == null) labelId = WorldmapSegment.TEMP_LABEL_KEY;
List<String> currentCoverage = worldmap.labelledMaps.get(labelId);
if (currentCoverage == null) {
worldmap.labelledMaps.put(labelId, new ArrayList<String>());
currentCoverage = worldmap.labelledMaps.get(labelId);
} else {
currentCoverage.clear();
}
for (int i = 0; i < msmListModel.getSize(); i++) {
if (msmListSelectionModel.isSelectedIndex(i)) {
if (!currentCoverage.contains(msmListModel.getElementAt(i).id)) {
currentCoverage.add(msmListModel.getElementAt(i).id);
}
}
}
mslmListModel.listChanged();
repaintMap();
}
});
removeFromCoverage.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (selectedLabel == null) return;
String labelId = selectedLabel.id;
if (labelId == null) labelId = WorldmapSegment.TEMP_LABEL_KEY;
List<String> currentCoverage = worldmap.labelledMaps.get(labelId);
if (currentCoverage == null) return;
List<String> toRemove = new ArrayList<String>();
for (int i = 0; i < mslmListModel.getSize(); i++) {
if (mslmListSelectionModel.isSelectedIndex(i)) {
if (currentCoverage.contains(mslmListModel.getElementAt(i).id)) {
toRemove.add(mslmListModel.getElementAt(i).id);
}
}
}
currentCoverage.removeAll(toRemove);
mslmListModel.listChanged();
repaintMap();
}
});
labelParametersPane.add(labelCoverageButtonsPane, JideBoxLayout.FIX);
setCurrentHighlightModel(mslmListModel);
}
public class MapSegmentMapsListModel implements ListModel<TMXMap> {
WorldmapSegment segment;
public MapSegmentMapsListModel(WorldmapSegment segment) {
this.segment = segment;
}
@Override
public int getSize() {
return segment.mapLocations.size();
}
@Override
public TMXMap getElementAt(int index) {
return segment.getProject().getMap(((String)segment.mapLocations.keySet().toArray()[index]));
}
public void listChanged() {
for (ListDataListener l : listeners) {
l.contentsChanged(new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, 0, getSize() - 1));
}
}
List<ListDataListener> listeners = new ArrayList<ListDataListener>();
@Override
public void addListDataListener(ListDataListener l) {
listeners.add(l);
}
@Override
public void removeListDataListener(ListDataListener l) {
listeners.remove(l);
}
}
public class MapSegmentLabelMapsListModel implements ListModel<TMXMap> {
WorldmapSegment segment;
WorldmapSegment.NamedArea area;
public MapSegmentLabelMapsListModel(WorldmapSegment segment, WorldmapSegment.NamedArea area) {
this.segment = segment;
this.area = area;
}
@Override
public int getSize() {
if (area.id == null) return segment.labelledMaps.get(WorldmapSegment.TEMP_LABEL_KEY).size();
return segment.labelledMaps.get(area.id).size();
}
@Override
public TMXMap getElementAt(int index) {
if (area.id == null) return segment.getProject().getMap(segment.labelledMaps.get(WorldmapSegment.TEMP_LABEL_KEY).get(index));
return segment.getProject().getMap(segment.labelledMaps.get(area.id).get(index));
}
public void listChanged() {
for (ListDataListener l : listeners) {
l.contentsChanged(new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, 0, getSize() - 1));
}
}
List<ListDataListener> listeners = new ArrayList<ListDataListener>();
@Override
public void addListDataListener(ListDataListener l) {
listeners.add(l);
}
@Override
public void removeListDataListener(ListDataListener l) {
listeners.remove(l);
}
}
public class MapSegmentLabelsListModel implements ListModel<WorldmapSegment.NamedArea> {
WorldmapSegment segment;
public MapSegmentLabelsListModel(WorldmapSegment segment) {
this.segment = segment;
}
@Override
public int getSize() {
return segment.labels.values().size();
}
@Override
public WorldmapSegment.NamedArea getElementAt(int index) {
return new ArrayList<WorldmapSegment.NamedArea>(segment.labels.values()).get(index);
}
public void listChanged() {
for (ListDataListener l : listeners) {
l.contentsChanged(new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, 0, getSize() - 1));
}
}
List<ListDataListener> listeners = new ArrayList<ListDataListener>();
@Override
public void addListDataListener(ListDataListener l) {
listeners.add(l);
}
@Override
public void removeListDataListener(ListDataListener l) {
listeners.remove(l);
}
}
public static class MapCellRenderer extends DefaultListCellRenderer {
private static final long serialVersionUID = 6819681566800482793L;
public MapCellRenderer() {
super();
}
@SuppressWarnings("rawtypes")
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (value == null) {
label.setText("None");
} else {
label.setText(((GameDataElement)value).getDesc());
if (((GameDataElement)value).getIcon() == null) {
Notification.addError("Unable to find icon for "+((GameDataElement)value).getDesc());
} else {
label.setIcon(new ImageIcon(((GameDataElement)value).getIcon()));
}
}
return label;
}
}
public static class MapLabelCellRenderer extends DefaultListCellRenderer {
private static final long serialVersionUID = 6819681566800482793L;
public MapLabelCellRenderer() {
super();
}
@SuppressWarnings("rawtypes")
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (value == null) {
label.setText("None");
} else {
WorldmapSegment.NamedArea area = (WorldmapSegment.NamedArea) value;
if (area.id != null) {
label.setText(area.name+" ("+area.id+")");
label.setIcon(new ImageIcon(DefaultIcons.getLabelIcon()));
} else {
label.setText("Incomplete Label. Enter an ID.");
label.setIcon(new ImageIcon(DefaultIcons.getNullifyIcon()));
}
}
return label;
}
}
public JPanel createButtonPane(final WorldmapSegment node) {
final JButton gdeIcon = new JButton(new ImageIcon(DefaultIcons.getUIMapImage()));
JPanel savePane = new JPanel();
@@ -721,14 +1012,108 @@ public class WorldMapEditor extends Editor {
public void pushToModel() {
mapView.pushToModel();
msmListModel.listChanged();
notifyModelModified();
updateXmlViewText(((WorldmapSegment)target).toXml());
}
public void notifyModelModified() {
target.state = GameDataElement.State.modified;
this.name = ((WorldmapSegment)target).getDesc();
target.childrenChanged(new ArrayList<ProjectTreeNode>());
}
ListSelectionListener activeSelectionListSelectionListener = new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
repaintMap();
};
};
private void setCurrentSelectionModel(ListModel<TMXMap> listModel, ListSelectionModel listSelectionModel) {
if (currentSelectionSelectionModel != null) {
currentSelectionSelectionModel.removeListSelectionListener(activeSelectionListSelectionListener);
}
currentSelectionListModel = listModel;
currentSelectionSelectionModel = listSelectionModel;
mapView.selectedListModel = listModel;
mapView.selectedSelectionModel = listSelectionModel;
currentSelectionSelectionModel.addListSelectionListener(activeSelectionListSelectionListener);
repaintMap();
}
private void setCurrentHighlightModel(ListModel<TMXMap> listModel) {
mapView.highlightedListModel = listModel;
repaintMap();
}
public void clearSelection() {
currentSelectionSelectionModel.clearSelection();
}
public void addToSelection(String mapId) {
if (mapId == null) return;
int index = -1;
for (int i = 0; i < currentSelectionListModel.getSize(); i++) {
if (currentSelectionListModel.getElementAt(i).id.equals(mapId)) {
index = i;
break;
}
}
currentSelectionSelectionModel.addSelectionInterval(index, index);
}
public void removeFromSelection(String mapId) {
if (mapId == null) return;
int index = -1;
for (int i = 0; i < currentSelectionListModel.getSize(); i++) {
if (currentSelectionListModel.getElementAt(i).id.equals(mapId)) {
index = i;
break;
}
}
currentSelectionSelectionModel.removeSelectionInterval(index, index);
}
public void repaintMap() {
mapView.revalidate();
mapView.repaint();
}
@Override
public void valueChanged(JComponent source, Object value) {
WorldmapSegment worldmap = (WorldmapSegment)target;
boolean changed = false;
if (source == labelIdField) {
List<String> coverage;
if (selectedLabel.id != null) {
coverage = worldmap.labelledMaps.get(selectedLabel.id);
worldmap.labelledMaps.remove(selectedLabel.id);
worldmap.labels.remove(selectedLabel.id);
} else {
coverage = worldmap.labelledMaps.get(WorldmapSegment.TEMP_LABEL_KEY);
worldmap.labels.remove(WorldmapSegment.TEMP_LABEL_KEY);
}
selectedLabel.id = (String) value;
if (value != null) {
worldmap.labelledMaps.put(selectedLabel.id, coverage);
worldmap.labels.put(selectedLabel.id, selectedLabel);
}
mslListModel.listChanged();
changed = true;
} else if (source == labelNameField) {
selectedLabel.name = (String) value;
mslListModel.listChanged();
changed = true;
repaintMap();
} else if (source == labelTypeField) {
selectedLabel.type = (String) value;
changed = true;
}
if (changed) {
notifyModelModified();
updateXmlViewText(worldmap.toXml());
}
}
}

Some files were not shown because too many files have changed in this diff Show More