Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: epinter/tqrespec
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.2.1
Choose a base ref
...
head repository: epinter/tqrespec
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v0.6.0
Choose a head ref

Commits on Oct 20, 2018

  1. Use eventHandler instead property bind

    Property bind can fail and doesn't trigger the listener depending what is being executed inside the Task.
    epinter committed Oct 20, 2018
    Copy the full SHA
    7e0f54c View commit details

Commits on May 12, 2019

  1. Copy the full SHA
    a71085c View commit details
  2. Version 0.2.1

    epinter committed May 12, 2019
    Copy the full SHA
    2daf3d3 View commit details

Commits on Jul 3, 2019

  1. Prepare branch for rebase, revert conflicting commits

    Revert "Support player.chr from TQ 2.1"
    
    This reverts commit a71085c.
    
    Revert "Version 0.2.1"
    
    This reverts commit 2daf3d3.
    epinter committed Jul 3, 2019
    Copy the full SHA
    ee2c547 View commit details

Commits on Jul 6, 2019

  1. Copy the full SHA
    b03e243 View commit details
  2. Update project files

    Upgrade gradle, add local libs directory
    epinter committed Jul 6, 2019
    Copy the full SHA
    9e97191 View commit details
  3. Add toString() to BlockInfo

    epinter committed Jul 6, 2019
    Copy the full SHA
    a597e60 View commit details
  4. Copy the full SHA
    344d56b View commit details
  5. Reformat PlayerParser

    epinter committed Jul 6, 2019
    Copy the full SHA
    6ffbd47 View commit details
  6. Copy the full SHA
    8a832c6 View commit details
  7. Copy the full SHA
    7c5a8b8 View commit details
  8. Reformat source code

    epinter committed Jul 6, 2019
    Copy the full SHA
    c6b4be3 View commit details
  9. Copy the full SHA
    6440eda View commit details
  10. Copy the full SHA
    33e993e View commit details
  11. Copy the full SHA
    8cf4801 View commit details
  12. Add support to load database

    epinter committed Jul 6, 2019
    Copy the full SHA
    67ce104 View commit details
  13. Copy the full SHA
    07c177c View commit details
  14. Copy the full SHA
    23a5075 View commit details
  15. Fix duplicate condition

    epinter committed Jul 6, 2019
    Copy the full SHA
    bc66ac5 View commit details
  16. Copy the full SHA
    d4f584c View commit details
  17. Copy the full SHA
    1d4b0b4 View commit details
  18. Copy the full SHA
    8ef0a24 View commit details
  19. Copy the full SHA
    b955392 View commit details
  20. Copy the full SHA
    0dcf644 View commit details
  21. Update gitignore

    epinter committed Jul 6, 2019
    Copy the full SHA
    adc66fa View commit details
  22. Copy the full SHA
    56a1498 View commit details
  23. Copy the full SHA
    f5d4639 View commit details
  24. Adjust preloader progress

    epinter committed Jul 6, 2019
    Copy the full SHA
    c82726f View commit details
  25. Upgrade commons-lang3

    epinter committed Jul 6, 2019
    Copy the full SHA
    25f8d2e View commit details
  26. Copy the full SHA
    b2f8b14 View commit details
  27. Copy the full SHA
    82f1be1 View commit details
  28. Update copyright

    epinter committed Jul 6, 2019
    Copy the full SHA
    28e7229 View commit details
  29. Copy the full SHA
    8158a7b View commit details
  30. Copy the full SHA
    9b8a05d View commit details
  31. Revert "Add tqdatabase project dependency"

    This reverts commit 7716ac6.
    epinter committed Jul 6, 2019
    Copy the full SHA
    246d50b View commit details
  32. Update gitignore

    epinter committed Jul 6, 2019
    Copy the full SHA
    49233fe View commit details
  33. Copy the full SHA
    cd60056 View commit details
  34. Update gitignore

    epinter committed Jul 6, 2019
    Copy the full SHA
    39c2991 View commit details
  35. Add tqdatabase dependency in a subdirectory

    The project git should be cloned inside this directory.
    epinter committed Jul 6, 2019
    Copy the full SHA
    6f88e2d View commit details
  36. Copy the full SHA
    3fc0ea8 View commit details
  37. Copy the full SHA
    f5d8b4b View commit details
  38. Copy the full SHA
    7f49d68 View commit details
  39. Add unit test for Version

    epinter committed Jul 6, 2019
    Copy the full SHA
    4347031 View commit details
  40. Copy the full SHA
    aa0368a View commit details
  41. Copy the full SHA
    212615e View commit details
  42. Add Skills tab

    epinter committed Jul 6, 2019
    Copy the full SHA
    c40a094 View commit details
  43. Copy the full SHA
    54f1256 View commit details
  44. Copy the full SHA
    80ec3f5 View commit details
  45. Copy the full SHA
    13fce9c View commit details
  46. Refactor PlayerData

    Move methods from SkillUtils to PlayerData
    Move blocks and variables data to dedicated class
    Rename SkillBlock class
    Move GameInfo and DeepClonable classes
    epinter committed Jul 6, 2019
    Copy the full SHA
    f61d629 View commit details
Showing with 4,095 additions and 1,698 deletions.
  1. +20 −1 .gitignore
  2. +9 −4 .idea/artifacts/tqrespec.xml
  3. +10 −0 .idea/inspectionProfiles/Project_Default.xml
  4. +7 −1 .idea/misc.xml
  5. +0 −8 .idea/modules.xml
  6. +0 −30 .idea/modules/tqrespec.iml
  7. +2 −0 .idea/vcs.xml
  8. +61 −0 CHANGELOG.md
  9. +88 −4 README.md
  10. BIN assets/screenshot_attributes.png
  11. BIN assets/screenshot_skills.png
  12. +297 −6 build.gradle
  13. +0 −44 dist/create_dist.cmd
  14. +0 −70 dist/jlink.cmd
  15. +160 −0 gradle/patchModulesJar.gradle
  16. +2 −6 gradle/wrapper/gradle-wrapper.properties
  17. 0 gradlew
  18. +1 −1 settings.gradle
  19. +99 −34 src/main/java/br/com/pinter/tqrespec/Main.java
  20. +3 −2 src/main/java/br/com/pinter/tqrespec/Settings.java
  21. +22 −0 src/main/java/br/com/pinter/tqrespec/core/EventHandlerWithException.java
  22. +15 −15 src/main/java/br/com/pinter/tqrespec/{ → core}/ExceptionHandler.java
  23. +29 −0 src/main/java/br/com/pinter/tqrespec/core/GuiceModule.java
  24. +68 −0 src/main/java/br/com/pinter/tqrespec/core/InjectionContext.java
  25. +6 −6 src/main/java/br/com/pinter/tqrespec/{ → core}/TaskWithException.java
  26. +4 −3 src/main/java/br/com/pinter/tqrespec/{ → core}/WorkerThread.java
  27. +2 −2 src/main/java/br/com/pinter/tqrespec/{ → core}/package-info.java
  28. +5 −4 src/main/java/br/com/pinter/tqrespec/gui/{ControllerAboutForm.java → AboutController.java}
  29. +88 −0 src/main/java/br/com/pinter/tqrespec/gui/AppPreloader.java
  30. +418 −0 src/main/java/br/com/pinter/tqrespec/gui/AttributesPaneController.java
  31. +0 −719 src/main/java/br/com/pinter/tqrespec/gui/ControllerMainForm.java
  32. +464 −0 src/main/java/br/com/pinter/tqrespec/gui/MainController.java
  33. +37 −48 src/main/java/br/com/pinter/tqrespec/gui/ResizeListener.java
  34. +64 −0 src/main/java/br/com/pinter/tqrespec/gui/SkillListCell.java
  35. +45 −0 src/main/java/br/com/pinter/tqrespec/gui/SkillListViewItem.java
  36. +293 −0 src/main/java/br/com/pinter/tqrespec/gui/SkillsPaneController.java
  37. +47 −0 src/main/java/br/com/pinter/tqrespec/gui/State.java
  38. +17 −4 src/main/java/br/com/pinter/tqrespec/save/BlockInfo.java
  39. +112 −68 src/main/java/br/com/pinter/tqrespec/save/ChangesTable.java
  40. +4 −5 src/main/java/br/com/pinter/tqrespec/{ → save}/DeepCloneable.java
  41. +208 −0 src/main/java/br/com/pinter/tqrespec/save/FileParser.java
  42. +5 −2 src/main/java/br/com/pinter/tqrespec/save/HeaderInfo.java
  43. +3 −2 src/main/java/br/com/pinter/tqrespec/save/IncompatibleSavegameException.java
  44. +249 −58 src/main/java/br/com/pinter/tqrespec/save/PlayerData.java
  45. +132 −240 src/main/java/br/com/pinter/tqrespec/save/PlayerParser.java
  46. +87 −0 src/main/java/br/com/pinter/tqrespec/save/PlayerSkill.java
  47. +47 −43 src/main/java/br/com/pinter/tqrespec/save/PlayerWriter.java
  48. +68 −0 src/main/java/br/com/pinter/tqrespec/save/SaveData.java
  49. +9 −6 src/main/java/br/com/pinter/tqrespec/save/VariableInfo.java
  50. +53 −0 src/main/java/br/com/pinter/tqrespec/tqdata/Db.java
  51. +63 −44 src/main/java/br/com/pinter/tqrespec/{ → tqdata}/GameInfo.java
  52. +52 −0 src/main/java/br/com/pinter/tqrespec/tqdata/Txt.java
  53. +18 −6 src/main/java/br/com/pinter/tqrespec/{ → util}/Constants.java
  54. +53 −14 src/main/java/br/com/pinter/tqrespec/{ → util}/Util.java
  55. +19 −20 src/main/java/br/com/pinter/tqrespec/{ → util}/Version.java
  56. +26 −0 src/main/java/module-info.java
  57. +0 −12 src/main/module-info.java
  58. +2 −2 src/main/resources/fxml/about.fxml
  59. +26 −2 src/main/resources/fxml/main.css
  60. +13 −96 src/main/resources/fxml/main.fxml
  61. +45 −0 src/main/resources/fxml/preloader.css
  62. BIN src/main/resources/fxml/preloader_bg.png
  63. +123 −0 src/main/resources/fxml/tab_points.fxml
  64. +79 −0 src/main/resources/fxml/tab_skills.fxml
  65. +11 −66 src/main/resources/i18n/UI.properties
  66. +48 −0 src/test/java/br/com/pinter/tqrespec/VersionTest.java
  67. +157 −0 src/test/java/br/com/pinter/tqrespec/save/PlayerParserTest.java
21 changes: 20 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
gamedata/
libs/
tqdatabase/
sdk/
*.arz
*.arc
Player.chr
hs_err_pid*
.gradle


#### https://github.com/github/gitignore/blob/master/Global/JetBrains.gitignore
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
@@ -10,6 +16,8 @@ hs_err_pid*
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
.idea/encodings.xml
.idea/vcs.xml

# Sensitive or high-churn files:
.idea/**/dataSources/
@@ -24,6 +32,16 @@ hs_err_pid*
.idea/**/gradle.xml
.idea/**/libraries

# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
.idea/modules.xml
.idea/*.iml
.idea/modules
*.iml
*.ipr

# CMake
cmake-build-debug/
cmake-build-release/
@@ -38,6 +56,7 @@ cmake-build-release/

# IntelliJ
out/
build/

# mpeltonen/sbt-idea plugin
.idea_modules/
13 changes: 9 additions & 4 deletions .idea/artifacts/tqrespec.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 0 additions & 8 deletions .idea/modules.xml

This file was deleted.

30 changes: 0 additions & 30 deletions .idea/modules/tqrespec.iml

This file was deleted.

2 changes: 2 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 61 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Changelog

## [0.6.0] - 2019-07-06
### Added
- Feature to reclaim points from all skills
- Feature to reclaim mastery points above level 1
- Added game text and database data parsing (for skills and class names)
- Distribution package creation and app-image automated in build system
- Added some needed unit tests

### Changed
- Improved errors and exceptions handling
- Updated from Java 9.0.4 to Java 11.0.2
- Updated JavaFX to 11.0
- Improved application startup
- Project and source code refactor, upgraded dependencies, Dependency Injection
- Build script refactor
- Added support for multiplatform development

### Fixed
- Small bug fixes and code warnings

### Removed
- Removed .jar and reduced .zip distribution formats
- Removed dist scripts

## [0.2.1] - 2019-05-12
### Added
- Compatibility with Titan Quest 2.1

### Fixed
- Fixed bug when loading character

## [0.2.0] - 2018-04-07
### Added
- Feature to copy character
- Added script to generate zip with app-image
- Discard all in memory player data when an exception occurs

### Changed
- Rework UI
- Added support to tabs on main window, for future
- Rework player parsing and in-memory patching of raw data

### Fixed
- Disable ui controls while saving or copying
- Encode player name properly, to support more characters and avoid bugs

## [0.1.2] - 2018-01-13
### Added
- GUI
- New version check
- Dist scripts

### Changed
- Data parsing rework
- Project and source code refactor

## [0.1.1] - 2018-01-07
### Added
- Initial testing version, core classes, without GUI
92 changes: 88 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,90 @@
# tqrespec
TQRespec - A respec tool for Titan Quest game
# TQ Respec

This tool allows the change of attributes (Health, Energy, Strength, Dexterity, Intelligence) of any character, any Titan Quest version.
[![Latest Release](https://img.shields.io/github/release/epinter/tqrespec.svg)](https://github.com/epinter/tqrespec/releases/latest)
[![Downloads](https://img.shields.io/github/downloads/epinter/tqrespec/total.svg)](https://github.com/epinter/tqrespec/releases/latest)
[![Release Date](https://img.shields.io/github/release-date/epinter/tqrespec.svg)](https://github.com/epinter/tqrespec/releases/latest)
[![Steam views](https://img.shields.io/steam/views/1262483108.svg)](https://steamcommunity.com/sharedfiles/filedetails/?id=1262483108)
[![License](https://img.shields.io/github/license/epinter/tqrespec.svg)](https://github.com/epinter/tqrespec/blob/master/LICENSE)

## Introduction

TQRespec is a tool for [Titan Quest](https://titanquestgame.com) game that helps you to change your character at any time. While this tool permits you make changes not available in game, cheating features are not available.

#### ***Download***

You can download TQRespec from the releases page, [here](https://github.com/epinter/tqrespec/releases/latest). There's no installation, just extract and run.

#### ***Requirements***

This software requires **Microsoft Windows 64-bit** (tested with Windows 7, 8, 8.1 and 10), **Windows 32-bit is not supported**. You don't need to have java or any other software installed, but if you have problems like missing dlls, check if you have Microsoft Visual C++ Redist 2015 installed.

## **How to use**

#### ***Before you start***

Keep in my that this software make modifications to your save game (more specifically the file Player.chr). You shouldn't run this software while the game is running, you can corrupt your save game. So, always close the game before opening TQRespec.

![TQRespec Screenshot 1](https://raw.githubusercontent.com/epinter/tqrespec/master/assets/screenshot_attributes.png "Attributes")
![TQRespec Screenshot 2](https://raw.githubusercontent.com/epinter/tqrespec/master/assets/screenshot_skills.png "Skills")

#### ***Use it!***

Select the character you want to change. Now you can see some information like Class and Difficulty, and start to change your savegame.

#### ***Attributes***

There are five attributes available to change (Health, Energy, Strength, Intelligence, Dexterity). When you decrease an attribute, you will see the number in "Available Points" increasing, and when you increase an attribute the points are automatically got from "Available Points". The number that increases in each of the attribute fields follows in game rules. In fact, you can't give more points to your character, just redistribute. So no cheat.

#### ***Skills***

Now if you change to Skills tab, you will see two lists with the skills of your character. Below this lists, there are two buttons for each. The first button "Reclaim All Skills Points" will remove all points allocated on skills on that mastery and make the points available to use in game. The button "Reclaim Mastery Points" permits you to reduce the mastery level. If you have a mastery on level 24 and 7 skills with points allocated, you can click on first button to have the points from the 7 skills back to you, then on the other button to have 23 points back from the mastery, so you will have the mastery with just 1 point allocated.

#### ***Copy your character***

If you want to change the name of you character, you can type the new name in the "New save" text field then click on "Copy To" button. A new character will be created, with progress, inventory with all items, attributes, skills, etc... The only thing you will not have on the copy is the Storage.

#### ***Saving and backup***

After you finish, you can click on Save. At this moment, TQRespec will make a backup of the file "Player.chr" inside a .zip in the folder "Titan Quest - Immortal Throne/SaveData/TQRespec Backup". The file "Player.chr" is where the games saves everything about the character, except the progress, transfer area and storage.

## ***Project and source code***

TQRespec is developed in Java language, using OpenJFX to provide graphical user interface, and a few more other open source dependencies. The current version is built using OpenJDK 11. The recommended version is OpenJDK with Hotspot from [AdoptOpenJDK.net](https://adoptopenjdk.net).
The software was made to work in Microsoft Windows operating system, since the game runs only in Windows. But because Java is multi-platform, this project is prepared to allow you to develop and test under Linux. To do so, you will need to have this structure inside the project (./gamedata):
~~~
gamedata/SaveData/Main/_savegame/Player.chr
gamedata/Database/database.arz
gamedata/Text/Text_EN.arc
~~~


#### ***Building***

Before building it, you need to have OpenJFX(JavaFX) SDK inside the ./sdk subdirectory. A repository with the needed files can be found [here](https://github.com/epinter/openjfx-sdk). Just clone it inside sdk directory.

With JDK 11 installed, you can build executing the 'clean' and 'build' gradle tasks with the command:

~~~
gradlew clean
gradlew build
~~~

#### ***Running the development version***

With this repository and openjfx-sdk properly in place, you can run with:

~~~
gradlew run
~~~



## **Troubleshooting**

#### ***Startup***
This software can fail to start if the game is not detected. Game is detected searching for Uninstall information from Windows, and data from your Steam installation. If you can't start, get the error message inside the "Show details" and open an issue. Or maybe you can find some help in the discussion at Steam Guides.


#### ***Errors and reporting***
If you see an error popup while using TQRespec, click on "Show details" and copy the complete error message (called java Exception). With this exception, a developer can find exactly where the code failed. If the software is crashing and you don't get an error message, or is crashing during startup, go to Windows Explorer and find the log file called **tqrespec.log** inside the directory %TEMP%. The %TEMP% is the windows temporary directory for you user.

There's no way to add points, only redistribute, so no cheat.
Binary file added assets/screenshot_attributes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/screenshot_skills.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
303 changes: 297 additions & 6 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -3,24 +3,315 @@
*/

group 'br.com.pinter.tqrespec'

apply plugin: 'application'
apply plugin: 'java'
apply plugin: 'com.github.johnrengelman.shadow'

sourceCompatibility = 1.9
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "com.github.jengelman.gradle.plugins:shadow:5.1.0"
classpath 'org.openjfx:javafx-plugin:0.0.7'
}
}

sourceSets {
main.output.resourcesDir = main.java.outputDir
}

ext.distAppName = 'TQRespec'
version = '0.6.0'
sourceCompatibility = 11
targetCompatibility = 11
ext.moduleName = 'br.com.pinter.tqrespec'
ext.javahome = System.properties['java.home']
ext.moduleInfoPresent = false
ext.javafxSdkLibDir = new File(projectDir, 'sdk/javafx-sdk-11/lib')
ext.javafxSdkBinDir = new File(projectDir, 'sdk/javafx-sdk-11/bin')
ext.patchjarDir = new File(buildDir, '/patchedjar')
ext.patchJars = false
apply from: "gradle/patchModulesJar.gradle"
if (file('src/main/java/module-info.java').exists()) {
ext.moduleInfoPresent = true
mainClassName = "$moduleName/br.com.pinter.tqrespec.Main"
} else {
mainClassName = "br.com.pinter.tqrespec.Main"
}

repositories {
mavenCentral()
}

dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile group: 'net.java.dev.jna', name: 'platform', version: '3.5.0'
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.7'
compile fileTree(dir: 'libs', include: '*.jar')
compile fileTree(dir: javafxSdkLibDir, include: '*.jar')
testImplementation group: 'junit', name: 'junit', version: '4.12'
testImplementation group: 'org.mockito', name: 'mockito-core', version: '2.28.2'
testImplementation group: 'org.powermock', name: 'powermock-module-junit4', version: '2.0.2'
testImplementation group: 'org.powermock', name: 'powermock-api-mockito2', version: '2.0.2'
compile group: 'com.google.inject', name: 'guice', version: '4.2.2'
compile group: 'net.java.dev.jna', name: 'jna-platform', version: '5.3.1'
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.9'
compile project(':tqdatabase')
}

import org.apache.tools.ant.taskdefs.condition.Os;

if (Os.isFamily(Os.FAMILY_WINDOWS)) {
ext.binjlink = javahome + "/bin/jlink.exe"
ext.binjava = javahome + "/bin/java.exe"
} else {
ext.binjlink = javahome + "/bin/jlink"
ext.binjava = javahome + "/bin/java"
}

jar {
manifest {
attributes 'Class-Path': configurations.compile.collect { it.getName() }.join(' ')
attributes('Class-Path': configurations.compile.collect { it.getName() }.join(' '),
'Implementation-Title': distAppName,
'Implementation-Version': project.version,
'Automatic-Module-Name': moduleName,
'Main-Class': 'br.com.pinter.tqrespec.Main')
}
// from('src/main/resources/META-INF')
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
shadowJar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
version = null
}

task copyMetaInf(type: Copy) {
from fileTree(new File(sourceSets.main.output.resourcesDir, '/META-INF'))
into new File(sourceSets.main.java.outputDir, '/META-INF')
}

if (moduleInfoPresent) {
compileJava {
inputs.property("moduleName", moduleName)
doFirst {
options.compilerArgs = [
'--module-path', classpath.asPath,
'--patch-module', "$moduleName=" + files(sourceSets.main.resources.srcDirs).asPath,
]
classpath = files()
}
}

run {
inputs.property("moduleName", moduleName)
doFirst {
jvmArgs = [
'--add-reads', 'br.com.pinter.tqrespec=ALL-UNNAMED',
'--add-opens', 'javafx.base/com.sun.javafx.reflect=ALL-UNNAMED',
'--add-opens', 'br.com.pinter.tqrespec/br.com.pinter.tqrespec=ALL-UNNAMED',
'--add-opens', 'br.com.pinter.tqrespec/br.com.pinter.tqrespec.core=ALL-UNNAMED',
'--add-opens', 'br.com.pinter.tqrespec/br.com.pinter.tqrespec.tqdata=ALL-UNNAMED',
'--add-opens', 'br.com.pinter.tqrespec/br.com.pinter.tqrespec.gui=ALL-UNNAMED',
'--add-opens', 'br.com.pinter.tqrespec/br.com.pinter.tqrespec.save=ALL-UNNAMED',
'--module-path', classpath.asPath,
'--class-path', classpath.asPath,
'--module', mainClassName,
'--patch-module', "$moduleName=" + files(sourceSets.main.output.resourcesDir).asPath,
]
classpath = files()
}
}
} else {
run {
doFirst {
jvmArgs = [
'--module-path', javafxSdkLibDir,
'--add-modules', 'javafx.base,javafx.fxml,javafx.graphics,javafx.controls'
]
}
}
}

task copyToLib(type: Copy) {
doFirst {
mkdir "$buildDir/jardist"
}
from(configurations.compile.findAll { !it.getName().matches("javafx.*.jar") }).collect { it.getPath() }

from jar
into "$buildDir/jardist"
}

copyToLib.dependsOn(jar)
classes.dependsOn('copyMetaInf')

task copyJarsToModuleDeps(type: Copy) {
from project(':tqdatabase').configurations.compile
from project.configurations.compile
into patchjarDir
}

task patchModulesJar(type: PatchJarModuleTask) {
dependsOn 'copyJarsToModuleDeps'
delete = true
tempPatchDir = new File(buildDir, '/temppatch')
tempJarDir = patchjarDir
moduleDeps = ['com.sun.jna.platform': 'com.sun.jna']
}

task copyAppimageFiles(type: Copy) {
dependsOn(jar)
doFirst {
mkdir "$buildDir/appimage-files"
}
if (!moduleInfoPresent) {
from project(':tqdatabase').tasks.jar.outputs.files.collect { it.getPath() }
from jar
}
from(configurations.compile.findAll {
!it.getName().matches("javafx.*.jar") && !it.getName().matches("tqdatabase.*.jar")
}).collect { it.getPath() }
into "$buildDir/appimage-files"
}

task copyAppimageModules(type: Copy) {
if (patchJars) {
dependsOn(patchModulesJar)
}

dependsOn(jar)
doFirst {
mkdir "$buildDir/appimage-modules"
}
from project(':tqdatabase').tasks.jar.outputs.files.collect { it.getPath() }
if (moduleInfoPresent) {
if (patchJars) {
from patchjarDir
}
from jar
}
into "$buildDir/appimage-modules"
}

task jlink(type: Exec) {
dependsOn clean
dependsOn 'jar'
dependsOn 'tqdatabase:jar'
dependsOn 'copyToLib'
dependsOn 'copyJarsToModuleDeps'
if (!patchJars) {
dependsOn 'copyAppimageFiles'
}
dependsOn 'copyAppimageModules'
workingDir buildDir

if (moduleInfoPresent) {
commandLine binjlink,
'--module-path', "${javafxSdkLibDir}" + File.pathSeparator + "${buildDir}/appimage-modules",
'--add-modules', ("br.com.pinter.tqdatabase,br.com.pinter.tqrespec,javafx.base,javafx.fxml,javafx.graphics,javafx.controls,java.prefs,java.base,jdk.zipfs,jdk.unsupported,java.logging"),
'--output', new File(buildDir, '/jre-image'),
'--strip-debug',
'--compress', '2',
'--no-header-files',
'--no-man-pages',
'--strip-native-commands'
} else {
commandLine binjlink,
'--module-path', "${javafxSdkLibDir}" + File.pathSeparator + "${buildDir}/appimage-modules",
'--add-modules', ("javafx.base,javafx.fxml,javafx.graphics,javafx.controls,java.prefs,java.base,jdk.zipfs,jdk.unsupported,java.logging"),
'--output', new File(buildDir, '/jre-image'),
'--strip-debug',
'--compress', '2',
'--no-header-files',
'--no-man-pages',
'--strip-native-commands'
}
}

task archiveAppimage(type: Zip) {
dependsOn 'appimage'
from "${buildDir}/app-image"
archiveFileName = "${distAppName}-${project.version}.zip"
destinationDirectory = file("$buildDir")
}

task appimage(type: Exec) {
dependsOn 'jlink'
workingDir buildDir
if (moduleInfoPresent) {
commandLine binjava,
"-Xmx1024M",
'--module-path', "${javahome}/jmods",
'--add-opens', 'jdk.jlink/jdk.tools.jlink.internal.packager=jdk.packager',
'--module', "jdk.packager/jdk.packager.Main",
'create-image',
'--verbose',
'--echo-mode',
'--singleton',
'--runtime-image', new File(buildDir, "jre-image"),
'--module', mainClassName,
'--output', new File(buildDir, '/app-image'),
'--name', distAppName,
'--icon', "${projectDir}/src/main/resources/icon/icon64.ico",
'--version', project.version,
'--vendor', 'Emerson Pinter',
'--input', new File(buildDir, '/appimage-files'),
'--description', 'The respec tool for Titan Quest game',
'--class', "br.com.pinter.tqrespec.Main",
'--jvm-args', '--add-opens javafx.base/com.sun.javafx.reflect=ALL-UNNAMED ' +
'--add-opens javafx.base/com.sun.javafx.reflect=ALL-UNNAMED ' +
'--add-opens br.com.pinter.tqrespec/br.com.pinter.tqrespec=ALL-UNNAMED ' +
'--add-opens br.com.pinter.tqrespec/br.com.pinter.tqrespec.core=ALL-UNNAMED ' +
'--add-opens br.com.pinter.tqrespec/br.com.pinter.tqrespec.tqdata=ALL-UNNAMED ' +
'--add-opens br.com.pinter.tqrespec/br.com.pinter.tqrespec.gui=ALL-UNNAMED ' +
'--add-opens br.com.pinter.tqrespec/br.com.pinter.tqrespec.save=ALL-UNNAMED ' +
'--add-reads br.com.pinter.tqrespec=ALL-UNNAMED'
} else {
commandLine binjava,
"-Xmx1024M",
'--module-path', "${javahome}/jmods",
'--add-opens', 'jdk.jlink/jdk.tools.jlink.internal.packager=jdk.packager',
'--module', "jdk.packager/jdk.packager.Main",
'create-image',
'--verbose',
'--echo-mode',
'--singleton',
'--module-path', "$buildDir/appimage-files",
'--runtime-image', new File(buildDir, "jre-image"),
'--input', new File(buildDir, '/appimage-files'),
'--main-jar', jar.outputs.files.collect { it.getName() }.get(0),
'--output', new File(buildDir, '/app-image'),
'--name', distAppName,
'--icon', "${projectDir}/src/main/resources/icon/icon64.ico",
'--version', project.version,
'--vendor', 'Emerson Pinter',
'--description', 'The respec tool for Titan Quest game',
'--class', mainClassName,
'--jvm-args', '--add-opens javafx.base/com.sun.javafx.reflect=ALL-UNNAMED'
}
doLast {
copy {
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
from javafxSdkBinDir.listFiles().findAll {
it.getName().equals("decora_sse.dll") ||
it.getName().equals("fxplugins.dll") ||
it.getName().equals("glass.dll") ||
it.getName().equals("javafx_font.dll") ||
it.getName().equals("javafx_iio.dll") ||
it.getName().equals("prism_common.dll") ||
it.getName().equals("prism_d3d.dll") ||
it.getName().equals("prism_sw.dll")
}.collect()
into "$buildDir/app-image/${distAppName}"
} else {
from javafxSdkLibDir.listFiles().findAll { it.getName().endsWith('.so') }.collect()
into "$buildDir/app-image/${distAppName}"
}
}
new File(buildDir, "app-image/${distAppName}/jfxwebkit.dll").delete()
new File(buildDir, "app-image/${distAppName}/ucrtbase.dll").delete()
new File(buildDir, "app-image/${distAppName}/gstreamer-lite.dll").delete()
new File(buildDir, "app-image/${distAppName}/glib-lite.dll").delete()
new File(buildDir, "jre-image").deleteDir()
}
}
44 changes: 0 additions & 44 deletions dist/create_dist.cmd

This file was deleted.

70 changes: 0 additions & 70 deletions dist/jlink.cmd

This file was deleted.

160 changes: 160 additions & 0 deletions gradle/patchModulesJar.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
ext.PatchJarModuleTask = PatchJarModuleTask

import java.util.regex.Matcher
import java.util.regex.Pattern

/*
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

class PatchJarModuleTask extends DefaultTask {
boolean debug
boolean delete = true
String tempPatchDir
String tempJarDir
Map<String, String> moduleDeps
List<String> patchedModules = new ArrayList<>()

private File fileTempPatchDir
private File fileTempJarDir
private final String JAR = findJava("jar")
private final String JDEPS = findJava("jdeps")
private final String JAVAC = findJava("javac")

static void printStream(InputStream stream) {
stream.eachLine {
System.err.println(it)
}
}

private static String findJava(String tool) {
return String.format("%2\$s%1\$sbin%1\$s%3\$s",
File.separator,
System.getProperty("java.home"),
tool,
OperatingSystemFamily.WINDOWS ? ".exe" : "")
}

@TaskAction
void start() {
fileTempPatchDir = new File(tempPatchDir)
fileTempJarDir = new File(tempJarDir)

fileTempPatchDir.deleteDir()
fileTempPatchDir.mkdir()

if (!fileTempJarDir.exists()) {
fileTempJarDir.mkdir()
}

new File(tempJarDir).eachFile {
if (it.getName().endsWith(".jar")) {
processFile(it.getPath())
}
}
}

boolean processFile(String filename) {
if (debug) System.err.println("Verifying jar " + filename)

Process jar = null
ProcessBuilder processBuilder = new ProcessBuilder()
processBuilder.command(JAR, "-d", "-f", filename)
jar = processBuilder.start()


BufferedReader stdout = new BufferedReader(new InputStreamReader(jar.getInputStream()))

String s = ""
String line
String packageName = null
while ((line = stdout.readLine()) != null) {
Pattern p = Pattern.compile("(^[^@]+)@.* automatic\$")
Matcher m = p.matcher(line)
if (m.matches() && packageName == null) {
s += line
packageName = m.group(1)
}
}
jar.waitFor()

if (packageName != null) {
System.out.println(String.format("Package found '%s' on file '%s', generating module-info", packageName, filename))
} else {
if (debug) System.out.println(String.format("No automatic module found on file '%s', ignoring", filename))
new File(filename).delete()
return
}

processBuilder = new ProcessBuilder()
processBuilder.directory(fileTempJarDir)
String addModules = ""

if (moduleDeps != null && moduleDeps.size() > 0 && moduleDeps.get(packageName) != null) {
addModules = "--add-modules="
for (Map.Entry<String, String> entry : moduleDeps.entrySet()) {
if (entry.key == packageName && entry.value != null && !entry.value.isEmpty()) {
addModules += entry.value.replace(" ", "")
}
}
}
processBuilder.command(JDEPS, "--module-path", tempJarDir, addModules, "--generate-module-info", tempPatchDir, filename)
if (debug) System.err.println("Running: " + processBuilder.command())

Process jdeps = processBuilder.start()
printStream(jdeps.getInputStream())
printStream(jdeps.getErrorStream())
jdeps.waitFor()

if (jdeps.exitValue() != 0) {
if(delete) {
new File(filename).delete()
}
return
}
if (debug) System.err.println("jdeps finished")

processBuilder = new ProcessBuilder()
processBuilder.directory(fileTempPatchDir)
processBuilder.command(JAVAC, "--module-path", tempJarDir, "--patch-module", String.format("%s=%s", packageName, filename),
String.format("%s/module-info.java", packageName), "-nowarn", "-d", ".")

if (debug) System.err.println("Running: " + processBuilder.command())
Process javac = processBuilder.start()
printStream(javac.getInputStream())
printStream(javac.getErrorStream())
javac.waitFor()
if (javac.exitValue() != 0) {
new File(filename).delete()
return
}
if (debug) System.err.println("javac finished")

processBuilder = new ProcessBuilder()
processBuilder.directory(fileTempPatchDir)
processBuilder.command(JAR, "--update", "--file", filename, "module-info.class")
if (debug) System.err.println("Running: " + processBuilder.command())
Process jarUpdate = processBuilder.start()
printStream(jarUpdate.getInputStream())
printStream(jarUpdate.getErrorStream())
jarUpdate.waitFor()
if (jarUpdate.exitValue() != 0) {
if (delete) {
new File(filename).delete()
}
return
}
fileTempPatchDir.deleteDir()
if (debug) System.err.println("jar update finished")
if (new File(filename).exists()) {
System.err.printf("JAR file '%s' (%s) patched\n", filename, packageName)
patchedModules.add(packageName)
return true
} else {
System.err.printf("JAR file '%s' (%s) discarded\n", filename, packageName)
return false
}

}

}
8 changes: 2 additions & 6 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
#
# Copyright (C) 2017 Emerson Pinter - All Rights Reserved
#

#Sun Dec 31 01:49:47 BRST 2017
#Sat Jun 29 14:35:37 BRT 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.2.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.5-all.zip
Empty file modified gradlew
100644 → 100755
Empty file.
2 changes: 1 addition & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
@@ -3,4 +3,4 @@
*/

rootProject.name = 'tqrespec'

include ':tqdatabase'
133 changes: 99 additions & 34 deletions src/main/java/br/com/pinter/tqrespec/Main.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2017 Emerson Pinter - All Rights Reserved
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

/* This file is part of TQ Respec.
@@ -20,16 +20,26 @@

package br.com.pinter.tqrespec;

import br.com.pinter.tqrespec.gui.ControllerMainForm;
import br.com.pinter.tqrespec.core.ExceptionHandler;
import br.com.pinter.tqrespec.core.GuiceModule;
import br.com.pinter.tqrespec.core.InjectionContext;
import br.com.pinter.tqrespec.gui.MainController;
import br.com.pinter.tqrespec.gui.ResizeListener;
import br.com.pinter.tqrespec.save.PlayerData;
import br.com.pinter.tqrespec.tqdata.Db;
import br.com.pinter.tqrespec.tqdata.Txt;
import br.com.pinter.tqrespec.util.Constants;
import br.com.pinter.tqrespec.util.Util;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.application.Preloader;
import javafx.beans.binding.Bindings;
import javafx.event.EventHandler;
import javafx.concurrent.Task;
import javafx.event.Event;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.TextArea;
import javafx.scene.image.Image;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
@@ -38,45 +48,110 @@
import javafx.stage.StageStyle;
import javafx.stage.WindowEvent;

import java.io.IOException;
import com.google.inject.Inject;

import java.io.*;
import java.util.Collections;
import java.util.ResourceBundle;

public class Main extends Application {
public static void main(String[] args) {
@Inject
private Db db;

@Inject
private Txt txt;

@Inject
private FXMLLoader fxmlLoader;

private InjectionContext injectionContext = new InjectionContext(this,
Collections.singletonList(new GuiceModule()));

public static void main(String... args) {
System.setProperty("javafx.preloader", "br.com.pinter.tqrespec.gui.AppPreloader");
launch(args);
}

private static double scale = 0;

private void load(Stage primaryStage) {
Task<Void> task = new Task<>() {
@Override
public Void call() {
//preload game database metadata and skills
notifyPreloader(new Preloader.ProgressNotification(0.3));
db.initialize();
notifyPreloader(new Preloader.ProgressNotification(0.7));
db.skills().preload();
//preload text
notifyPreloader(new Preloader.ProgressNotification(0.9));
txt.preload();
return null;
}
};
task.setOnFailed(e -> {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("Error");
alert.setHeaderText("Error loading application");
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
e.getSource().getException().printStackTrace(printWriter);
TextArea textArea = new TextArea(stringWriter.toString());
textArea.setMaxWidth(Double.MAX_VALUE);
textArea.setMaxHeight(Double.MAX_VALUE);
alert.getDialogPane().setExpandableContent(textArea);
alert.showAndWait();
System.exit(1);
});

task.setOnSucceeded(e -> {
notifyPreloader(new Preloader.ProgressNotification(1.0));
primaryStage.show();
});
new Thread(task).start();
primaryStage.setOnShown(windowEvent -> notifyPreloader(new Preloader.StateChangeNotification(
Preloader.StateChangeNotification.Type.BEFORE_START)));

}


@Override
public void start(Stage primaryStage) {
Font.loadFont(getClass().getResourceAsStream("/fxml/albertus-mt.ttf"),16);
Font.loadFont(getClass().getResourceAsStream("/fxml/albertus-mt-light.ttf"),16);
public void start(Stage primaryStage) throws Exception {
//close stderr before initialize guice, we want to hide java warning about reflection
System.err.close();
injectionContext.initialize();
try {
System.setErr(new PrintStream(new FileOutputStream(
new File(System.getProperty("java.io.tmpdir"), "tqrespec.log"))));
}catch (Exception ignored) {
}
notifyPreloader(new Preloader.ProgressNotification(0.1));
prepareMainStage(primaryStage);
load(primaryStage);
}

public void prepareMainStage(Stage primaryStage) {
Font.loadFont(getClass().getResourceAsStream("/fxml/albertus-mt.ttf"), 16);
Font.loadFont(getClass().getResourceAsStream("/fxml/albertus-mt-light.ttf"), 16);
Thread.setDefaultUncaughtExceptionHandler(ExceptionHandler::unhandled);

ResourceBundle bundle = ResourceBundle.getBundle("i18n.UI");
Parent root = null;
Parent root;
try {
root = FXMLLoader.load(getClass().getResource("/fxml/main.fxml"), bundle);
fxmlLoader.setResources(ResourceBundle.getBundle("i18n.UI"));
fxmlLoader.setLocation(getClass().getResource("/fxml/main.fxml"));
root = fxmlLoader.load();
} catch (IOException e) {
e.printStackTrace();
return;
}

primaryStage.setTitle(Util.getBuildTitle());
primaryStage.getIcons().addAll(new Image("icon/icon64.png"),new Image("icon/icon32.png"),new Image("icon/icon16.png"));
primaryStage.getIcons().addAll(new Image("icon/icon64.png"), new Image("icon/icon32.png"), new Image("icon/icon16.png"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);

//disable alt+f4
Platform.setImplicitExit(false);
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
event.consume();
}
});
primaryStage.setOnCloseRequest(Event::consume);

//remove default window decoration
String osName = System.getProperty("os.name");
@@ -102,23 +177,13 @@ public void handle(WindowEvent event) {
primaryStage.setMaxHeight(root.maxHeight(-1));
primaryStage.setMaxWidth(root.maxWidth(-1));

primaryStage.addEventHandler(KeyEvent.KEY_PRESSED, (new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent event) {
if (event.getCode() == KeyCode.ESCAPE) {
Util.closeApplication();
}
primaryStage.addEventHandler(KeyEvent.KEY_PRESSED, (event -> {
if (event.getCode() == KeyCode.ESCAPE) {
Util.closeApplication();
}
}));

//handler to prepare controls on startup, the use of initialize and risk of crash
primaryStage.addEventHandler(WindowEvent.WINDOW_SHOWN, new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent window) {
ControllerMainForm.mainFormInitialized.setValue(true);
}
});

primaryStage.show();
primaryStage.addEventHandler(WindowEvent.WINDOW_SHOWN, window -> MainController.mainFormInitialized.setValue(true));
}
}
5 changes: 3 additions & 2 deletions src/main/java/br/com/pinter/tqrespec/Settings.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 Emerson Pinter - All Rights Reserved
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

/* This file is part of TQ Respec.
@@ -22,6 +22,7 @@

import java.util.prefs.Preferences;

@SuppressWarnings("unused")
public class Settings {
public static void setAlwaysFullBackup(boolean alwaysFullBackup) {
Preferences prefs = Preferences.userNodeForPackage(Settings.class);
@@ -31,6 +32,6 @@ public static void setAlwaysFullBackup(boolean alwaysFullBackup) {
public static boolean getAlwaysFullBackup() {
Preferences prefs = Preferences.userNodeForPackage(Settings.class);

return prefs.getBoolean("always_fullbackup",false);
return prefs.getBoolean("always_fullbackup", false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

package br.com.pinter.tqrespec.core;

import javafx.event.Event;
import javafx.event.EventHandler;

@FunctionalInterface
public interface EventHandlerWithException<T extends Event> extends EventHandler<T> {
@Override
default void handle(T t) {
try {
handleEvent(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

void handleEvent(T t);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 Emerson Pinter - All Rights Reserved
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

/* This file is part of TQ Respec.
@@ -18,43 +18,43 @@
along with TQ Respec. If not, see <http://www.gnu.org/licenses/>.
*/

package br.com.pinter.tqrespec;
package br.com.pinter.tqrespec.core;

import br.com.pinter.tqrespec.save.PlayerData;
import javafx.application.Platform;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.TextArea;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.stage.Modality;
import org.apache.commons.lang3.exception.ExceptionUtils;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Optional;

@SuppressWarnings("unused")
public class ExceptionHandler {
public static void unhandled(Thread t, Throwable e) {
e.printStackTrace();
PlayerData.getInstance().reset();
if(Platform.isFxApplicationThread()) {
ExceptionHandler.showAlert(t,e);

if (Platform.isFxApplicationThread()) {
ExceptionHandler.showAlert(t, e);
}
}

public static void showAlert(Thread t, Throwable e) {
String header = e.toString();
header = header.replaceAll("^java.lang.RuntimeException: (.*Exception.*)","$1");
StringWriter stackTrace = new StringWriter();
e.printStackTrace(new PrintWriter(stackTrace));
private static void showAlert(Thread t, Throwable e) {
String header = ExceptionUtils.getRootCause(e).toString();
if(header == null) {
header = e.toString();
}
header = header.replaceAll("^java.lang.RuntimeException: (.*)", "$1");

Alert alert = new Alert(Alert.AlertType.ERROR);
alert.initModality(Modality.APPLICATION_MODAL);
alert.setTitle("Error");
alert.setHeaderText("An unhandled exception occurred");
alert.setContentText(header);

TextArea textArea = new TextArea(stackTrace.toString());
TextArea textArea = new TextArea(ExceptionUtils.getStackTrace(e.getCause()));
textArea.setEditable(false);
textArea.setWrapText(false);

@@ -74,7 +74,7 @@ public static void showAlert(Thread t, Throwable e) {


Optional<ButtonType> result = alert.showAndWait();
if(result.get() == abort) {
if (result.isPresent() && result.get() == abort) {
Platform.exit();
System.exit(0);
}
29 changes: 29 additions & 0 deletions src/main/java/br/com/pinter/tqrespec/core/GuiceModule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

/* This file is part of TQ Respec.
TQ Respec is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
TQ Respec is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TQ Respec. If not, see <http://www.gnu.org/licenses/>.
*/

package br.com.pinter.tqrespec.core;

import com.google.inject.AbstractModule;

public class GuiceModule extends AbstractModule {
@Override
protected void configure() {
}
}
68 changes: 68 additions & 0 deletions src/main/java/br/com/pinter/tqrespec/core/InjectionContext.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

/* This file is part of TQ Respec.
TQ Respec is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
TQ Respec is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TQ Respec. If not, see <http://www.gnu.org/licenses/>.
*/

package br.com.pinter.tqrespec.core;

import com.google.inject.*;
import com.google.inject.Module;
import javafx.fxml.FXMLLoader;

import java.util.ArrayList;
import java.util.List;

public class InjectionContext {
private Object context;
private Injector guiceInjector;
private List<Module> modules;

public InjectionContext(Object context, List<Module> modules) {
this.context = context;
this.modules = modules;
}

public void initialize() {
List<Module> modulesList = new ArrayList<>(modules);
modulesList.add(new FXMLProvider());
guiceInjector = Guice.createInjector(modulesList.toArray(new Module[0]));
injectMembers(context);

}

public void injectMembers(Object instance) {
guiceInjector.injectMembers(instance);
}

public <T> T getInstance(Class<T> type) {
return guiceInjector.getInstance(type);
}

private class FXMLProvider extends AbstractModule {
@Override
protected void configure() {
}

@Provides
FXMLLoader producer() {
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setControllerFactory(aClass -> getInstance(aClass));
return fxmlLoader;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 Emerson Pinter - All Rights Reserved
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

/* This file is part of TQ Respec.
@@ -18,17 +18,17 @@
along with TQ Respec. If not, see <http://www.gnu.org/licenses/>.
*/

package br.com.pinter.tqrespec;
package br.com.pinter.tqrespec.core;

import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.EventHandler;

public abstract class TaskWithException<T> extends Task {
public abstract class TaskWithException<T> extends Task<T> {

public TaskWithException() {
super();
this.setOnFailed((WorkerStateEvent) -> {throw new RuntimeException(this.getException());});
this.setOnFailed((WorkerStateEvent) -> {
throw new RuntimeException(this.getException());
});
}

@Override
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 Emerson Pinter - All Rights Reserved
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

/* This file is part of TQ Respec.
@@ -18,9 +18,10 @@
along with TQ Respec. If not, see <http://www.gnu.org/licenses/>.
*/

package br.com.pinter.tqrespec;
package br.com.pinter.tqrespec.core;

public class WorkerThread extends Thread {
public class WorkerThread extends Thread {
@SuppressWarnings("unused")
public WorkerThread() {
super();
this.setUncaughtExceptionHandler(ExceptionHandler::unhandled);
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2017 Emerson Pinter - All Rights Reserved
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

/* This file is part of TQ Respec.
@@ -18,4 +18,4 @@
along with TQ Respec. If not, see <http://www.gnu.org/licenses/>.
*/

package br.com.pinter.tqrespec;
package br.com.pinter.tqrespec.core;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 Emerson Pinter - All Rights Reserved
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

/* This file is part of TQ Respec.
@@ -20,9 +20,10 @@

package br.com.pinter.tqrespec.gui;

import br.com.pinter.tqrespec.Util;
import br.com.pinter.tqrespec.util.Util;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
@@ -32,7 +33,8 @@
import java.net.URL;
import java.util.ResourceBundle;

public class ControllerAboutForm implements Initializable {
@SuppressWarnings("unused")
public class AboutController implements Initializable {
@FXML
private AnchorPane rootelement;

@@ -54,5 +56,4 @@ public void closeAboutWindow(MouseEvent evt) {
Stage stage = (Stage) rootelement.getScene().getWindow();
stage.close();
}

}
88 changes: 88 additions & 0 deletions src/main/java/br/com/pinter/tqrespec/gui/AppPreloader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

/* This file is part of TQ Respec.
TQ Respec is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
TQ Respec is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TQ Respec. If not, see <http://www.gnu.org/licenses/>.
*/

package br.com.pinter.tqrespec.gui;

import javafx.application.Preloader;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.image.Image;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

@SuppressWarnings("RedundantThrows")
public class AppPreloader extends Preloader {
private ProgressBar bar;
private Stage stage;

private Scene createPreloaderScene() {
BorderPane pane = new BorderPane();
Scene scene = new Scene(pane, 370, 210);
pane.getStylesheets().add("/fxml/preloader.css");
pane.getStyleClass().add("bg-container");

bar = new ProgressBar(0.1);
bar.getStyleClass().add("bar");
pane.setCenter(bar);

Label title = new Label();
title.setText("TQRespec");
title.getStyleClass().add("tq-bigtitle");
pane.setTop(title);
BorderPane.setAlignment(title, Pos.CENTER);

ProgressIndicator indicator = new ProgressIndicator();
indicator.getStyleClass().add("indicator");
pane.setBottom(indicator);

return scene;

}

@Override
public void start(Stage stage) throws Exception {
this.stage = stage;
this.stage.getIcons().addAll(new Image("icon/icon64.png"), new Image("icon/icon32.png"), new Image("icon/icon16.png"));
this.stage.setScene(createPreloaderScene());

String osName = System.getProperty("os.name");
if (osName != null && osName.startsWith("Windows")) {
this.stage.initStyle(StageStyle.TRANSPARENT);
} else {
this.stage.initStyle(StageStyle.UNDECORATED);
}

this.stage.show();
}

@Override
public void handleApplicationNotification(PreloaderNotification pn) {
if (pn instanceof ProgressNotification) {
bar.setProgress(((ProgressNotification) pn).getProgress());
} else if (pn instanceof StateChangeNotification) {
//hide after get any state update from application
stage.hide();
}
}
}
418 changes: 418 additions & 0 deletions src/main/java/br/com/pinter/tqrespec/gui/AttributesPaneController.java

Large diffs are not rendered by default.

719 changes: 0 additions & 719 deletions src/main/java/br/com/pinter/tqrespec/gui/ControllerMainForm.java

This file was deleted.

464 changes: 464 additions & 0 deletions src/main/java/br/com/pinter/tqrespec/gui/MainController.java

Large diffs are not rendered by default.

85 changes: 37 additions & 48 deletions src/main/java/br/com/pinter/tqrespec/gui/ResizeListener.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 Emerson Pinter - All Rights Reserved
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

/* This file is part of TQ Respec.
@@ -23,31 +23,26 @@
import javafx.beans.binding.Bindings;
import javafx.event.EventHandler;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;

import java.util.Locale;
@SuppressWarnings("CanBeFinal")
public class ResizeListener implements EventHandler<MouseEvent> {
private Stage stage;

public class ResizeListener implements EventHandler<MouseEvent>{
Stage stage;

double dx;
double dy;
double deltaX;
double deltaY;
double border = 10;
boolean resizeH = false;
boolean resizeV = false;
private double dx;
private double dy;
private boolean resizeH = false;
private boolean resizeV = false;

public ResizeListener(Stage stage) {
this.stage = stage;
}

@Override
public void handle(MouseEvent t) {
if(t.getY() < 27) {
if(!MouseEvent.MOUSE_PRESSED.equals(t.getEventType())
if (t.getY() < 27) {
if (!MouseEvent.MOUSE_PRESSED.equals(t.getEventType())
&& !MouseEvent.MOUSE_DRAGGED.equals(t.getEventType())) {
stage.getScene().setCursor(Cursor.DEFAULT);
}
@@ -56,62 +51,56 @@ public void handle(MouseEvent t) {
resizeV = false;
return;
}
if(MouseEvent.MOUSE_MOVED.equals(t.getEventType())){
if(t.getX() < border && t.getY() > stage.getScene().getHeight() -border){
if (MouseEvent.MOUSE_MOVED.equals(t.getEventType())) {
double border = 10;
if (t.getX() < border && t.getY() > stage.getScene().getHeight() - border) {
stage.getScene().setCursor(Cursor.SW_RESIZE);
resizeH = true;
resizeV = true;
}
else if(t.getX() > stage.getScene().getWidth() -border && t.getY() < border){
} else if (t.getX() > stage.getScene().getWidth() - border && t.getY() < border) {
stage.getScene().setCursor(Cursor.NE_RESIZE);
resizeH = true;
resizeV = true;
}
else if(t.getX() > stage.getScene().getWidth() -border && t.getY() > stage.getScene().getHeight() -border){
} else if (t.getX() > stage.getScene().getWidth() - border && t.getY() > stage.getScene().getHeight() - border) {
stage.getScene().setCursor(Cursor.SE_RESIZE);
resizeH = true;
resizeV = true;
}
else if(t.getX() < border || t.getX() > stage.getScene().getWidth() -border){
} else if (t.getX() < border || t.getX() > stage.getScene().getWidth() - border) {
stage.getScene().setCursor(Cursor.E_RESIZE);
resizeH = true;
resizeV = false;
}
else if(t.getY() > stage.getScene().getHeight() -border){
} else if (t.getY() > stage.getScene().getHeight() - border) {
stage.getScene().setCursor(Cursor.N_RESIZE);
resizeH = false;
resizeV = true;
}
else{
} else {
stage.getScene().setCursor(Cursor.DEFAULT);
resizeH = false;
resizeV = false;
}
}
else if(MouseEvent.MOUSE_PRESSED.equals(t.getEventType())){
} else if (MouseEvent.MOUSE_PRESSED.equals(t.getEventType())) {
dx = stage.getWidth() - t.getX();
dy = stage.getHeight() - t.getY();
}
else if(MouseEvent.MOUSE_DRAGGED.equals(t.getEventType())){
stage.getScene().getRoot().styleProperty().bind(Bindings.format("-fx-font-size: %sem;",stage.getWidth()/stage.getMinWidth()));
if (
((stage.getHeight() < stage.getMinHeight() && t.getY() + dy - stage.getHeight() > 0)
|| stage.getHeight() >= stage.getMinHeight()) &&
} else if (MouseEvent.MOUSE_DRAGGED.equals(t.getEventType())) {
stage.getScene().getRoot().styleProperty().bind(Bindings.format("-fx-font-size: %sem;", stage.getWidth() / stage.getMinWidth()));
if (
((stage.getHeight() < stage.getMinHeight() && t.getY() + dy - stage.getHeight() > 0)
|| stage.getHeight() >= stage.getMinHeight()) &&

((stage.getWidth() < stage.getMinWidth() && t.getX() + dx - stage.getWidth() > 0)
|| stage.getWidth() >= stage.getMinWidth())) {
if(resizeH) {
double newW = t.getX() + dx;
if(newW > stage.getMaxWidth()) return;
stage.setWidth(newW);
stage.setHeight(stage.getWidth() * (stage.getMinHeight() / stage.getMinWidth()));
}else if(resizeV) {
double newH = t.getY() + dy;
if(newH > stage.getMaxHeight()) return;
stage.setHeight(newH);
stage.setWidth(stage.getHeight()*(stage.getMinWidth()/stage.getMinHeight()));
}
((stage.getWidth() < stage.getMinWidth() && t.getX() + dx - stage.getWidth() > 0)
|| stage.getWidth() >= stage.getMinWidth())) {
if (resizeH) {
double newW = t.getX() + dx;
if (newW > stage.getMaxWidth()) return;
stage.setWidth(newW);
stage.setHeight(stage.getWidth() * (stage.getMinHeight() / stage.getMinWidth()));
} else if (resizeV) {
double newH = t.getY() + dy;
if (newH > stage.getMaxHeight()) return;
stage.setHeight(newH);
stage.setWidth(stage.getHeight() * (stage.getMinWidth() / stage.getMinHeight()));
}
}
}
}
}
64 changes: 64 additions & 0 deletions src/main/java/br/com/pinter/tqrespec/gui/SkillListCell.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

/* This file is part of TQ Respec.
TQ Respec is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
TQ Respec is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TQ Respec. If not, see <http://www.gnu.org/licenses/>.
*/

package br.com.pinter.tqrespec.gui;

import br.com.pinter.tqrespec.tqdata.Db;
import br.com.pinter.tqrespec.tqdata.Txt;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;

import com.google.inject.Inject;

public class SkillListCell extends ListCell<SkillListViewItem> {
private HBox container;
private Label skillName;
private Label skillPoints;

@Inject
private Txt txt;

SkillListCell() {
super();
skillName = new Label();
skillPoints = new Label();
HBox containerName = new HBox(skillName);
HBox containerPoints = new HBox(skillPoints);
container = new HBox(containerName, containerPoints);
HBox.setHgrow(containerName, Priority.ALWAYS);
}

@Override
protected void updateItem(SkillListViewItem s, boolean empty) {
super.updateItem(s, empty);
if (empty) {
setGraphic(null);
skillName.setText(null);
skillPoints.setText(null);
} else if (s != null) {
skillName.setText(s.getSkillNameText());
skillPoints.setText(String.valueOf(s.getSkillPoints()));
setGraphic(container);
}
}

}
45 changes: 45 additions & 0 deletions src/main/java/br/com/pinter/tqrespec/gui/SkillListViewItem.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

/* This file is part of TQ Respec.
TQ Respec is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
TQ Respec is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TQ Respec. If not, see <http://www.gnu.org/licenses/>.
*/

package br.com.pinter.tqrespec.gui;

public class SkillListViewItem {
private String skillName;
private int skillPoints;
private String skillNameText;

SkillListViewItem(String skillName, int skillPoints, String skillNameText) {
this.skillName = skillName;
this.skillPoints = skillPoints;
this.skillNameText = skillNameText;
}

String getSkillName() {
return skillName;
}

int getSkillPoints() {
return skillPoints;
}

public String getSkillNameText() {
return skillNameText;
}
}
293 changes: 293 additions & 0 deletions src/main/java/br/com/pinter/tqrespec/gui/SkillsPaneController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
/*
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

/* This file is part of TQ Respec.
TQ Respec is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
TQ Respec is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TQ Respec. If not, see <http://www.gnu.org/licenses/>.
*/

package br.com.pinter.tqrespec.gui;

import br.com.pinter.tqdatabase.models.Skill;
import br.com.pinter.tqrespec.save.PlayerData;
import br.com.pinter.tqrespec.save.PlayerSkill;
import br.com.pinter.tqrespec.tqdata.Db;
import br.com.pinter.tqrespec.tqdata.Txt;
import br.com.pinter.tqrespec.util.Util;
import com.google.inject.Inject;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.input.MouseEvent;
import javafx.util.Callback;

import java.net.URL;
import java.util.List;
import java.util.ResourceBundle;

public class SkillsPaneController implements Initializable {
@Inject
private Db db;

@Inject
private Txt txt;

@Inject
private PlayerData playerData;

@FXML
private ListView<SkillListViewItem> firstMasteryListView;

@FXML
private ListView<SkillListViewItem> secondMasteryListView;

@FXML
private Label firstMasteryLabel;

@FXML
private Label secondMasteryLabel;

@FXML
private Button reclaimMasteryFirstButton;

@FXML
private Button reclaimMasterySecondButton;

@FXML
private Button reclaimSkillsFirstButton;

@FXML
private Button reclaimSkillsSecondButton;

@FXML
private Label freeSkillPointsLabel;

private SimpleStringProperty currentSkillPoints = null;

private BooleanProperty saveDisabled = new SimpleBooleanProperty();

@Override
public void initialize(URL url, ResourceBundle resourceBundle) {

}

public boolean isSaveDisabled() {
return saveDisabled.get();
}

public BooleanProperty saveDisabledProperty() {
return saveDisabled;
}

public void setSaveDisabled(boolean saveDisabled) {
this.saveDisabled.set(saveDisabled);
}

public void loadCharEventHandler() {
currentSkillPoints = new SimpleStringProperty();

freeSkillPointsLabel.textProperty().bind(
Bindings.createStringBinding(() -> Util.getUIMessage("skills.availableSkillPoints",
currentSkillPoints.getValue()),
currentSkillPoints
)
);

updateMasteries();
}

private void resetSkilltabControls() {
firstMasteryLabel.setText(null);
firstMasteryListView.getItems().clear();
secondMasteryLabel.setText(null);
secondMasteryListView.getItems().clear();
firstMasteryListView.addEventFilter(MouseEvent.MOUSE_PRESSED, Event::consume);
secondMasteryListView.addEventFilter(MouseEvent.MOUSE_PRESSED, Event::consume);
disableControls(true);
}

protected void disableControls(boolean disable) {
firstMasteryListView.setDisable(disable);
secondMasteryListView.setDisable(disable);
if (!disable && firstMasteryListView.getItems().size() > 0) {
reclaimSkillsFirstButton.setDisable(false);
} else {
reclaimSkillsFirstButton.setDisable(true);
}
if (playerData.isCharacterLoaded() && (!disable && getMasteryLevel(0) > 1) && firstMasteryListView.getItems().size() == 0) {
reclaimMasteryFirstButton.setDisable(false);
} else {
reclaimMasteryFirstButton.setDisable(true);
}

if (!disable && secondMasteryListView.getItems().size() > 0) {
reclaimSkillsSecondButton.setDisable(false);
} else {
reclaimSkillsSecondButton.setDisable(true);
}
if (playerData.isCharacterLoaded() && (!disable && getMasteryLevel(1) > 1) && secondMasteryListView.getItems().size() == 0) {
reclaimMasterySecondButton.setDisable(false);
} else {
reclaimMasterySecondButton.setDisable(true);
}
}

protected void updateMasteries() {
if (!playerData.isCharacterLoaded()) {
return;
}
resetSkilltabControls();

fillMastery(0);
fillMastery(1);
disableControls(false);


currentSkillPoints.setValue(String.valueOf(playerData.getAvailableSkillPoints()));
}

private int getMasteryLevel(int i) {
List<Skill> masteries = playerData.getPlayerMasteries();

if (!(masteries.size() == 1 && i > 0) && masteries.size() > 0) {
PlayerSkill sb = playerData.getPlayerSkills().get(masteries.get(i).getRecordPath());
return playerData.getMasteryLevel(sb);
} else {
return -1;
}
}

private boolean fillMastery(int i) {
boolean ret = false;
Label masteryLabel;
ListView<SkillListViewItem> masteryListView;

switch (i) {
case 0:
masteryLabel = firstMasteryLabel;
masteryListView = firstMasteryListView;
break;
case 1:
masteryLabel = secondMasteryLabel;
masteryListView = secondMasteryListView;
break;
default:
return false;
}

Skill mastery = null;

if (!(playerData.getPlayerMasteries().size() == 1 && i > 0) && playerData.getPlayerMasteries().size() > 0) {
mastery = playerData.getPlayerMasteries().get(i);
masteryLabel.setText(
String.format("%s (%d)",
txt.getString(mastery.getSkillDisplayName()),
playerData.getPlayerSkills().get(mastery.getRecordPath()).getSkillLevel()
)
);
ret = true;
}

ObservableList<SkillListViewItem> observableSkills = createObservableListFromMastery(mastery);

Callback<ListView<SkillListViewItem>, ListCell<SkillListViewItem>> listViewCallback
= skillListView -> new SkillListCell();

masteryListView.setItems(observableSkills);
masteryListView.setCellFactory(listViewCallback);
return ret;
}

private ObservableList<SkillListViewItem> createObservableListFromMastery(Skill mastery) {
ObservableList<SkillListViewItem> ret = FXCollections.observableArrayList();

if (mastery == null) return ret;

for (Skill s : playerData.getPlayerSkillsFromMastery(mastery)) {
Skill s1 = s;
if (s.isPointsToPet() || s.isPointsToBuff()) {
s1 = db.skills().getSkill(s.getRecordPath(), true);
}

PlayerSkill sb = playerData.getPlayerSkills().get(s.getRecordPath());
if (sb == null || s1.getRecordPath() == null) continue;
ret.add(new SkillListViewItem(s1.getSkillDisplayName(),
sb.getSkillLevel(), txt.getString(s1.getSkillDisplayName())));
}
return ret;
}

private void reclaimPointsFromSkills(Skill mastery) throws Exception {
for (Skill s : playerData.getPlayerSkillsFromMastery(mastery)) {
PlayerSkill sb = playerData.getPlayerSkills().get(s.getRecordPath());
if (sb == null || s.getRecordPath() == null) continue;
playerData.reclaimSkillPoints(sb);
}
}

public void reclaimMasteryFirst(Event event) throws Exception {
disableControls(true);
Skill mastery = playerData.getPlayerMasteries().get(0);
PlayerSkill sb = playerData.getPlayerSkills().get(mastery.getRecordPath());

List<Skill> list = playerData.getPlayerSkillsFromMastery(mastery);
if (list.size() > 0) {
Util.showInformation(Util.getUIMessage("skills.removeSkillsBefore"), null);
return;
}

playerData.reclaimMasteryPoints(sb);
updateMasteries();
}

public void reclaimMasterySecond(Event event) throws Exception {
disableControls(true);
Skill mastery = playerData.getPlayerMasteries().get(1);
PlayerSkill sb = playerData.getPlayerSkills().get(mastery.getRecordPath());

List<Skill> list = playerData.getPlayerSkillsFromMastery(mastery);
if (list.size() > 0) {
Util.showInformation(Util.getUIMessage("skills.removeSkillsBefore"), null);
return;
}

playerData.reclaimMasteryPoints(sb);
updateMasteries();
}

public void reclaimSkillsFirst(Event event) throws Exception {
disableControls(true);
Skill mastery = playerData.getPlayerMasteries().get(0);
reclaimPointsFromSkills(mastery);
updateMasteries();
}

public void reclaimSkillsSecond(Event event) throws Exception {
disableControls(true);
Skill mastery = playerData.getPlayerMasteries().get(1);
reclaimPointsFromSkills(mastery);
updateMasteries();
}
}
47 changes: 47 additions & 0 deletions src/main/java/br/com/pinter/tqrespec/gui/State.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

/* This file is part of TQ Respec.
TQ Respec is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
TQ Respec is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TQ Respec. If not, see <http://www.gnu.org/licenses/>.
*/

package br.com.pinter.tqrespec.gui;

public class State {
private static State instance = null;
private Boolean saveInProgress = null;


public static State get() {
if (instance == null) {
synchronized (State.class) {
if (instance == null) {
instance = new State();
}
}
}
return instance;
}

public Boolean getSaveInProgress() {
return saveInProgress;
}

public void setSaveInProgress(Boolean saveInProgress) {
this.saveInProgress = saveInProgress;
}

}
21 changes: 17 additions & 4 deletions src/main/java/br/com/pinter/tqrespec/save/BlockInfo.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 Emerson Pinter - All Rights Reserved
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

/* This file is part of TQ Respec.
@@ -20,9 +20,11 @@

package br.com.pinter.tqrespec.save;

import java.io.Serializable;
import java.util.Hashtable;

public class BlockInfo {
@SuppressWarnings("unused")
public class BlockInfo implements Serializable {
private String tag = null;
private int start = -1;
private int end = -1;
@@ -61,11 +63,22 @@ public void setSize(int size) {
this.size = size;
}

public Hashtable<String, VariableInfo> getVariables() {
Hashtable<String, VariableInfo> getVariables() {
return variables;
}

public void setVariables(Hashtable<String, VariableInfo> variables) {
void setVariables(Hashtable<String, VariableInfo> variables) {
this.variables = variables;
}

@Override
public String toString() {
return "BlockInfo{" +
"tag='" + tag + '\'' +
", start=" + start +
", end=" + end +
", size=" + size +
", variables=" + variables +
'}';
}
}
180 changes: 112 additions & 68 deletions src/main/java/br/com/pinter/tqrespec/save/ChangesTable.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 Emerson Pinter - All Rights Reserved
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

/* This file is part of TQ Respec.
@@ -17,15 +17,14 @@
You should have received a copy of the GNU General Public License
along with TQ Respec. If not, see <http://www.gnu.org/licenses/>.
*/
package br.com.pinter.tqrespec;
package br.com.pinter.tqrespec.save;

import br.com.pinter.tqrespec.save.ChangesTable;
import org.apache.commons.lang3.exception.CloneFailedException;

import java.io.*;

public interface DeepCloneable extends Serializable {
default public Object deepClone() {
default Object deepClone() {
try {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
@@ -35,7 +34,7 @@ default public Object deepClone() {
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return objectInputStream.readUnshared();
} catch (IOException | ClassNotFoundException e) {
throw new CloneFailedException("deep clone failed");
throw new CloneFailedException("deep clone failed",e);
}
}
}
208 changes: 208 additions & 0 deletions src/main/java/br/com/pinter/tqrespec/save/FileParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
/*
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

/* This file is part of TQ Respec.
TQ Respec is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
TQ Respec is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TQ Respec. If not, see <http://www.gnu.org/licenses/>.
*/

package br.com.pinter.tqrespec.save;

import br.com.pinter.tqrespec.util.Constants;
import br.com.pinter.tqrespec.util.Util;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Hashtable;

abstract class FileParser {
private final static boolean DBG = false;

abstract Hashtable<String, VariableInfo> parseBlock(BlockInfo blockInfo);

abstract ByteBuffer getBuffer();

abstract void prepareBufferForRead();

abstract void parse() throws Exception;

int inventoryStart = -1;

abstract protected Hashtable<String, ArrayList<Integer>> getVariableLocation();

protected void reset() {
inventoryStart = -1;
}

void putVarIndex(String varName, int blockStart) {
this.getVariableLocation().computeIfAbsent(varName, k -> new ArrayList<>());
this.getVariableLocation().get(varName).add(blockStart);
}

private Hashtable<String, byte[]> blockTag = new Hashtable<>() {{
put("begin_block", new byte[]{0x0B, 0x00, 0x00, 0x00, 0x62, 0x65, 0x67, 0x69, 0x6E, 0x5F, 0x62, 0x6C, 0x6F, 0x63, 0x6B});
put("end_block", new byte[]{0x09, 0x00, 0x00, 0x00, 0x65, 0x6E, 0x64, 0x5F, 0x62, 0x6C, 0x6F, 0x63, 0x6B});
}};

int getBlockTagSize(String tag) {
int blockTagSize = blockTag.get(tag).length;
return (blockTagSize + 4);
}

Hashtable<Integer, BlockInfo> parseAllBlocks() {
if (inventoryStart != -1) {
throw new IllegalStateException("Illegal inventory offset, value was not reset on load");
}

Hashtable<Integer, BlockInfo> ret = new Hashtable<>();
int nextBlock = 0;
while (nextBlock >= 0) {
BlockInfo blockInfo = this.getNextBlock(nextBlock);

if (blockInfo == null) {
break;
}
if (DBG)
Util.log(String.format("nextBlock=%d; blockStart=%d; blockEnd=%d; blockSize=%d", nextBlock, blockInfo.getStart(), blockInfo.getEnd(), blockInfo.getSize()));

if (blockInfo.getSize() < 4) continue;

if ((blockInfo.getStart() >= inventoryStart && Constants.SKIP_INVENTORY_BLOCKS && inventoryStart > 0)) {
break;
}

Hashtable<String, VariableInfo> variables = parseBlock(blockInfo);
blockInfo.setVariables(variables);
ret.put(blockInfo.getStart(), blockInfo);
nextBlock = blockInfo.getEnd();
}

return ret;
}

private BlockInfo getNextBlock(int offset) {
int blockStart = searchBlockTag("begin_block", offset);
int blockEnd = searchBlockTag("end_block", blockStart) + getBlockTagSize("end_block");

if (blockStart > 0 && blockEnd > 0) {
int size = blockEnd - blockStart;
BlockInfo blockInfo = new BlockInfo();
blockInfo.setStart(blockStart);
blockInfo.setEnd(blockEnd);
blockInfo.setSize(size);
return blockInfo;
}
return null;
}

int searchBlockTag(String tag, int offset) {
int count = 0;
for (int i = offset; i >= 0 && i < this.getBuffer().capacity(); i++) {
Byte b = this.getBuffer().get(i);
byte[] blockTagBytes = this.blockTag.get(tag);
if (b.equals(blockTagBytes[count])) {
int blockTagOffset = i - count;
if (++count == blockTagBytes.length) {
return blockTagOffset;
}
} else if (count > 0) {
//outside a blocktag the count needs to be 0
count = 0;
if (b.equals(blockTagBytes[count])) {
count++;
}
}

}
return -1;
}

void readString(VariableInfo variableInfo, ByteBuffer byteBuffer) {
this.readString(variableInfo, byteBuffer, false);
}

void readString(VariableInfo variableInfo, ByteBuffer byteBuffer, boolean utf16le) {
if (variableInfo.getValSize() != -1) {
System.err.println("BUG: variable size != 0");
return;
}
try {
int len = byteBuffer.getInt();
variableInfo.setVariableType(VariableInfo.VariableType.String);
variableInfo.setValSize(len);
if (len <= 0) {
return;
}
if (utf16le) {
len *= 2;
}

byte[] buf = new byte[len];

byteBuffer.get(buf, 0, len);

variableInfo.setValue(new String(buf, utf16le ? "UTF-16LE" : "UTF-8"));
} catch (IOException e) {
e.printStackTrace();
}
}

void readInt(VariableInfo variableInfo) {
if (variableInfo.getValSize() != -1) {
System.err.println("BUG: variable size != 0");
return;
}

variableInfo.setValue(getBuffer().getInt());
variableInfo.setVariableType(VariableInfo.VariableType.Integer);
variableInfo.setValSize(4);
}

void readFloat(VariableInfo variableInfo) {
if (variableInfo.getValSize() != -1) {
System.err.println("BUG: variable size != 0");
return;
}

variableInfo.setValue(getBuffer().getFloat());
variableInfo.setVariableType(VariableInfo.VariableType.Float);
variableInfo.setValSize(4);
}

String readString(ByteBuffer byteBuffer) {
return this.readString(byteBuffer, false);
}

@SuppressWarnings({"WeakerAccess", "SameParameterValue"})
String readString(ByteBuffer byteBuffer, boolean utf16le) {
try {
int len = byteBuffer.getInt();
if (len <= 0) {
return null;
}
if (utf16le) {
len *= 2;
}
byte[] buf = new byte[len];

byteBuffer.get(buf, 0, len);
return new String(buf, utf16le ? "UTF-16LE" : "UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
7 changes: 5 additions & 2 deletions src/main/java/br/com/pinter/tqrespec/save/HeaderInfo.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 Emerson Pinter - All Rights Reserved
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

/* This file is part of TQ Respec.
@@ -20,7 +20,10 @@

package br.com.pinter.tqrespec.save;

public class HeaderInfo {
import java.io.Serializable;

@SuppressWarnings("unused")
public class HeaderInfo implements Serializable {
private int headerVersion = -1;
private String playerCharacterClass = null;
private String playerClassTag = null;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2017 Emerson Pinter - All Rights Reserved
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

/* This file is part of TQ Respec.
@@ -20,7 +20,8 @@

package br.com.pinter.tqrespec.save;

public class IncompatibleSavegameException extends Exception {
@SuppressWarnings("unused")
class IncompatibleSavegameException extends Exception {
public IncompatibleSavegameException() {
super();
}
307 changes: 249 additions & 58 deletions src/main/java/br/com/pinter/tqrespec/save/PlayerData.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2017 Emerson Pinter - All Rights Reserved
* Copyright (C) 2019 Emerson Pinter - All Rights Reserved
*/

/* This file is part of TQ Respec.
@@ -20,35 +20,48 @@

package br.com.pinter.tqrespec.save;

import br.com.pinter.tqdatabase.Database;
import br.com.pinter.tqdatabase.models.Skill;
import br.com.pinter.tqrespec.gui.State;
import br.com.pinter.tqrespec.tqdata.Db;
import br.com.pinter.tqrespec.tqdata.Txt;
import br.com.pinter.tqrespec.util.Constants;
import br.com.pinter.tqrespec.util.Util;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.apache.commons.lang3.StringUtils;

import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;

@SuppressWarnings("unused")
@Singleton
public class PlayerData {
@Inject
private Db db;

@Inject
private Txt txt;

@Inject
private SaveData saveData;

@Inject
private ChangesTable changes;

private static PlayerData instance = null;
private String playerName = null;
private Path playerChr = null;
private ByteBuffer buffer = null;
private Hashtable<Integer, BlockInfo> blockInfo = null;
private HeaderInfo headerInfo = null;
private Hashtable<String, ArrayList<Integer>> variableLocation = null;
private ChangesTable changes = null;
private Boolean saveInProgress = null;
private boolean isCustomQuest = false;
private final LinkedHashMap<String, PlayerSkill> playerSkills;

public static PlayerData getInstance() {
if (instance == null) {
synchronized (PlayerData.class) {
if (instance == null) {
instance = new PlayerData();
instance.changes = new ChangesTable();
instance.variableLocation = new Hashtable<String, ArrayList<Integer>>();
instance.blockInfo = new Hashtable<Integer, BlockInfo>();
}
}
}
return instance;
public PlayerData() {
playerSkills = new LinkedHashMap<>();
}

public String getPlayerName() {
@@ -67,80 +80,258 @@ public void setBuffer(ByteBuffer buffer) {
this.buffer = buffer;
}

public Hashtable<Integer, BlockInfo> getBlockInfo() {
return blockInfo;
public ChangesTable getChanges() {
return changes;
}

public void setBlockInfo(Hashtable<Integer, BlockInfo> blockInfo) {
this.blockInfo = blockInfo;
public Path getPlayerChr() {
return playerChr;
}

public HeaderInfo getHeaderInfo() {
return headerInfo;
public void setPlayerChr(Path playerChr) {
this.playerChr = playerChr;
}

public void setHeaderInfo(HeaderInfo headerInfo) {
this.headerInfo = headerInfo;
public boolean isCustomQuest() {
return isCustomQuest;
}

public Hashtable<String, ArrayList<Integer>> getVariableLocation() {
return variableLocation;
public void setCustomQuest(boolean customQuest) {
isCustomQuest = customQuest;
}

public void setVariableLocation(Hashtable<String, ArrayList<Integer>> variableLocation) {
this.variableLocation = variableLocation;
public String getPlayerClassTag() {
if (saveData.getHeaderInfo() != null) {
return saveData.getHeaderInfo().getPlayerClassTag();
}
return null;
}

public ChangesTable getChanges() {
return changes;
void prepareSkillsList() {
playerSkills.clear();
for (String v : saveData.getVariableLocation().keySet()) {
if (v.startsWith(Database.Variables.PREFIX_SKILL_NAME)) {
for (int blockOffset : saveData.getVariableLocation().get(v)) {
BlockInfo b = saveData.getBlockInfo().get(blockOffset);
if (changes.get(b.getStart()) != null
&& changes.get(b.getStart()).length == 0) {
//new block size is zero, was removed, ignore
continue;
}

PlayerSkill sb = new PlayerSkill();
sb.setSkillName((String) b.getVariables().get(Constants.Save.SKILL_NAME).getValue());
sb.setSkillEnabled((Integer) b.getVariables().get(Constants.Save.SKILL_ENABLED).getValue());
sb.setSkillActive((Integer) b.getVariables().get(Constants.Save.SKILL_ACTIVE).getValue());
sb.setSkillSubLevel((Integer) b.getVariables().get(Constants.Save.SKILL_SUB_LEVEL).getValue());
sb.setSkillTransition((Integer) b.getVariables().get(Constants.Save.SKILL_TRANSITION).getValue());
sb.setSkillLevel(changes.getInt(b.getStart(), Constants.Save.SKILL_LEVEL));
sb.setBlockStart(b.getStart());
if (sb.getSkillName() != null) {
synchronized (playerSkills) {
playerSkills.put(Objects.requireNonNull(Util.normalizeRecordPath(sb.getSkillName())),
sb);
}
}
}
}
}
}

public Path getPlayerChr() {
return playerChr;
public boolean isCharacterLoaded() {
return getBuffer() != null;
}

public void setPlayerChr(Path playerChr) {
this.playerChr = playerChr;
public int getAvailableSkillPoints() {
if (!isCharacterLoaded()) return 0;

int block = saveData.getVariableLocation().get("skillPoints").get(0);
BlockInfo statsBlock = saveData.getBlockInfo().get(block);
return changes.getInt(statsBlock.getStart(), "skillPoints");
}

public Boolean getSaveInProgress() {
return saveInProgress;
public LinkedHashMap<String, PlayerSkill> getPlayerSkills() {
boolean update = false;

for (PlayerSkill b : playerSkills.values()) {
if (changes.get(b.getBlockStart()) != null
&& changes.get(b.getBlockStart()).length == 0) {
//new block size is zero, was removed, ignore
update = true;
}
}

if (playerSkills.isEmpty() || update) {
prepareSkillsList();
}

return playerSkills;
}

public void setSaveInProgress(Boolean saveInProgress) {
this.saveInProgress = saveInProgress;
public int getMasteryLevel(PlayerSkill sb) {
int blockStart = sb.getBlockStart();
Skill mastery = db.skills().getSkill(sb.getSkillName(), false);
if (!mastery.isMastery()) {
throw new IllegalStateException("Error reclaiming points. Skill detected.");
}
BlockInfo sk = saveData.getBlockInfo().get(blockStart);
VariableInfo varSkillLevel = sk.getVariables().get("skillLevel");

if (varSkillLevel.getVariableType() == VariableInfo.VariableType.Integer) {
return changes.getInt(blockStart, "skillLevel");
}
return -1;
}

public boolean isCustomQuest() {
return isCustomQuest;
public void reclaimSkillPoints(PlayerSkill sb) throws Exception {
int blockStart = sb.getBlockStart();
Skill skill = db.skills().getSkill(sb.getSkillName(), false);
if (skill.isMastery()) {
throw new IllegalStateException("Error reclaiming points. Mastery detected.");
}

BlockInfo skillToRemove = saveData.getBlockInfo().get(blockStart);
VariableInfo varSkillLevel = skillToRemove.getVariables().get("skillLevel");
if (varSkillLevel.getVariableType() == VariableInfo.VariableType.Integer) {
int currentSkillPoints = changes.getInt("skillPoints");
int currentSkillLevel = (int) varSkillLevel.getValue();
changes.setInt("skillPoints", currentSkillPoints + currentSkillLevel);
changes.removeBlock(blockStart);
changes.setInt("max", changes.getInt("max") - 1);

if (changes.get(blockStart) != null
&& changes.get(blockStart).length == 0) {
prepareSkillsList();
}
}
}

public void setCustomQuest(boolean customQuest) {
isCustomQuest = customQuest;
public void reclaimMasteryPoints(PlayerSkill sb) throws Exception {
int blockStart = sb.getBlockStart();
Skill mastery = db.skills().getSkill(sb.getSkillName(), false);
if (!mastery.isMastery()) {
throw new IllegalStateException("Error reclaiming points. Not a mastery.");
}

int currentSkillPoints = changes.getInt("skillPoints");
int currentSkillLevel = changes.getInt(blockStart, "skillLevel");
if (currentSkillLevel > 1) {
changes.setInt("skillPoints", currentSkillPoints + (currentSkillLevel - 1));
changes.setInt(blockStart, "skillLevel", 1);
prepareSkillsList();
}
}

public boolean loadPlayerData(String playerName) throws Exception {
if (PlayerData.getInstance().getSaveInProgress() != null && PlayerData.getInstance().getSaveInProgress()) {
return false;
public List<Skill> getPlayerMasteries() {
List<Skill> ret = new ArrayList<>();
for (PlayerSkill sb : getPlayerSkills().values()) {
Skill skill = db.skills().getSkill(sb.getSkillName(), false);
if (skill != null && skill.isMastery()) {
ret.add(skill);
}
}
try {
new PlayerParser(playerName);
return true;
} catch (Exception e) {
throw e;
return ret;
}

public Boolean getSaveInProgress() {
return State.get().getSaveInProgress();
}

public void setSaveInProgress(Boolean saveInProgress) {
State.get().setSaveInProgress(saveInProgress);
}

public List<Skill> getPlayerSkillsFromMastery(Skill mastery) {
List<Skill> ret = new ArrayList<>();
for (PlayerSkill sb : getPlayerSkills().values()) {
Skill skill = db.skills().getSkill(sb.getSkillName(), false);
if (skill != null && !skill.isMastery() && skill.getParentPath().equals(mastery.getRecordPath())) {
ret.add(skill);
}
}
return ret;
}

public int getStr() {
return Math.round(changes.getFloat("str"));
}

public void setStr(int val) throws Exception {
changes.setFloat("str", val);
}

public int getInt() {
return Math.round(changes.getFloat("int"));
}

public void setInt(int val) throws Exception {
changes.setFloat("int", val);
}

public int getDex() {
return Math.round(changes.getFloat("dex"));
}

public void setDex(int val) throws Exception {
changes.setFloat("dex", val);
}

public int getLife() {
return Math.round(changes.getFloat("life"));
}

public void setLife(int val) throws Exception {
changes.setFloat("life", val);
}

public int getMana() {
return Math.round(changes.getFloat("mana"));
}

public void setMana(int val) throws Exception {
changes.setFloat("mana", val);
}

public int getModifierPoints() {
return Math.round(changes.getInt("modifierPoints"));
}

public void setModifierPoints(int val) throws Exception {
changes.setInt("modifierPoints", val);
}

public int getXp() {
return changes.getInt("currentStats.experiencePoints");
}

public int getLevel() {
return changes.getInt("currentStats.charLevel");
}

public int getMoney() {
return changes.getInt("money");
}

public String getPlayerClassName() {
String charClass = getPlayerClassTag();
if (StringUtils.isNotEmpty(charClass)) {
return txt.getString(charClass);
}
return charClass;
}

public int getDifficulty() {
return changes.getInt("difficulty");
}

public void reset() {
this.buffer = null;
this.headerInfo = null;
this.blockInfo = new Hashtable<Integer, BlockInfo>();
this.playerName = null;
this.variableLocation = variableLocation = new Hashtable<String, ArrayList<Integer>>();
this.changes = new ChangesTable();
this.changes.clear();
this.playerChr = null;
this.saveInProgress = null;
State.get().setSaveInProgress(null);
saveData.reset();
}


}
Loading