Эх сурвалжийг харах

Signal handler to get optimization progress for GUI.
The GUI is now a propper python package.
Better code organization and file naming.
Small fixes to the update command.

Iñigo Valentin 4 жил өмнө
parent
commit
c658e8a166
77 өөрчлөгдсөн 829 нэмэгдсэн , 637 устгасан
  1. 1 0
      .gitignore
  2. 8 8
      Makefile
  3. 1 1
      README.md
  4. 0 195
      RuneOptimizerGUI/RuneOptimizer.py
  5. 1 1
      doxygen-cli.conf
  6. 1 1
      doxygen-gui.conf
  7. 1 1
      src/cli/runeoptimizer/db/db.c
  8. 3 3
      src/cli/runeoptimizer/db/db.h
  9. 0 0
      src/cli/runeoptimizer/error/error.h
  10. 0 0
      src/cli/runeoptimizer/gui/gui.c
  11. 0 0
      src/cli/runeoptimizer/gui/gui.h
  12. 1 1
      src/cli/runeoptimizer/help/help.c
  13. 0 0
      src/cli/runeoptimizer/help/help.h
  14. 91 53
      src/cli/runeoptimizer/optimize/optimize.c
  15. 1 1
      src/cli/runeoptimizer/optimize/optimize.h
  16. 1 1
      src/cli/runeoptimizer/player/player.c
  17. 0 0
      src/cli/runeoptimizer/player/player.h
  18. 4 4
      src/cli/runeoptimizer/runeoptimizer.c
  19. 1 1
      src/cli/runeoptimizer/runeoptimizer.h
  20. 1 1
      src/cli/runeoptimizer/team/team.c
  21. 3 3
      src/cli/runeoptimizer/team/team.h
  22. 1 1
      src/cli/runeoptimizer/team/team_add_unit.c
  23. 2 2
      src/cli/runeoptimizer/team/team_create.c
  24. 3 3
      src/cli/runeoptimizer/team/team_delete.c
  25. 2 2
      src/cli/runeoptimizer/team/team_list.c
  26. 1 1
      src/cli/runeoptimizer/team/team_remove_unit.c
  27. 4 4
      src/cli/runeoptimizer/unit/unit.c
  28. 0 0
      src/cli/runeoptimizer/unit/unit.h
  29. 1 1
      src/cli/runeoptimizer/update/update.c
  30. 5 5
      src/cli/runeoptimizer/update/update.h
  31. 9 9
      src/cli/runeoptimizer/update/update_current_stats.c
  32. 1 1
      src/cli/runeoptimizer/update/update_db_tables.c
  33. 5 9
      src/cli/runeoptimizer/update/update_efficiency.c
  34. 2 2
      src/cli/runeoptimizer/update/update_get_monster_name.c
  35. 3 2
      src/cli/runeoptimizer/update/update_info.c
  36. 68 69
      src/cli/runeoptimizer/update/update_json.c
  37. 1 1
      src/cli/runeoptimizer/update/update_rune_totals.c
  38. 1 1
      src/cli/test/runeoptimizer_e2e_test.c
  39. 1 1
      src/cli/test/runeoptimizer_integration_test.c
  40. 1 1
      src/cli/test/runeoptimizer_test.c
  41. 1 1
      src/cli/test/runeoptimizer_unit_test.c
  42. 0 0
      src/cli/test/unity/unity.c
  43. 0 0
      src/cli/test/unity/unity.h
  44. 0 0
      src/cli/test/unity/unity_internals.h
  45. 0 0
      src/gui/runeoptimizer_gui/__init__.py
  46. 0 0
      src/gui/runeoptimizer_gui/data/__init__.py
  47. 20 0
      src/gui/runeoptimizer_gui/data/data.py
  48. 0 0
      src/gui/runeoptimizer_gui/database/__init__.py
  49. 20 0
      src/gui/runeoptimizer_gui/database/database.py
  50. 59 56
      src/gui/runeoptimizer_gui/entity/Rune.py
  51. 16 14
      src/gui/runeoptimizer_gui/entity/RuneStat.py
  52. 27 27
      src/gui/runeoptimizer_gui/entity/StatSet.py
  53. 15 13
      src/gui/runeoptimizer_gui/entity/Team.py
  54. 23 17
      src/gui/runeoptimizer_gui/entity/Unit.py
  55. 11 9
      src/gui/runeoptimizer_gui/entity/UnitTeam.py
  56. 0 0
      src/gui/runeoptimizer_gui/entity/__init__.py
  57. 3 1
      src/gui/runeoptimizer_gui/gui/DialogConfirm.py
  58. 7 4
      src/gui/runeoptimizer_gui/gui/DialogNewTeam.py
  59. 6 2
      src/gui/runeoptimizer_gui/gui/DialogUpdateJson.py
  60. 9 6
      src/gui/runeoptimizer_gui/gui/PanelInfo.py
  61. 86 21
      src/gui/runeoptimizer_gui/gui/PanelOptimizer.py
  62. 49 29
      src/gui/runeoptimizer_gui/gui/PanelResults.py
  63. 23 17
      src/gui/runeoptimizer_gui/gui/PanelTeams.py
  64. 11 8
      src/gui/runeoptimizer_gui/gui/PanelUnits.py
  65. 19 15
      src/gui/runeoptimizer_gui/gui/RuneOptimizerFrame.py
  66. 15 7
      src/gui/runeoptimizer_gui/gui/TabList.py
  67. 0 0
      src/gui/runeoptimizer_gui/gui/__init__.py
  68. 0 0
      src/gui/runeoptimizer_gui/res/icon/help.png
  69. 0 0
      src/gui/runeoptimizer_gui/res/icon/info.png
  70. 0 0
      src/gui/runeoptimizer_gui/res/icon/optimize.png
  71. 0 0
      src/gui/runeoptimizer_gui/res/icon/progress.gif
  72. 0 0
      src/gui/runeoptimizer_gui/res/icon/results.png
  73. 0 0
      src/gui/runeoptimizer_gui/res/icon/teams.png
  74. 0 0
      src/gui/runeoptimizer_gui/res/icon/units.png
  75. 106 0
      src/gui/runeoptimizer_gui/runeoptimizer_gui.py
  76. 0 0
      src/gui/runeoptimizer_gui/values/__init__.py
  77. 73 0
      src/gui/runeoptimizer_gui/values/values.py

+ 1 - 0
.gitignore

@@ -9,6 +9,7 @@
 .pydevproject
 .pydevproject
 .project
 .project
 .settings
 .settings
+.directory
 *.sqlite
 *.sqlite
 *.xcf
 *.xcf
 *.json
 *.json

+ 8 - 8
Makefile

@@ -20,12 +20,12 @@ else
 endif
 endif
 
 
 # Files to compile
 # Files to compile
-# For tests, RuneOptimizer.c is not included in the list: is is included from
+# For tests, runeoptimizer.c is not included in the list: is is included from
 # each test file with a redefinition of main.
 # each test file with a redefinition of main.
-FILES=src/RuneOptimizer/RuneOptimizer.c src/RuneOptimizer/*/*.c
-TESTU_FILES=src/test/RuneOptimizerUnitTest.c src/RuneOptimizer/*/*.c
-TESTI_FILES=src/test/RuneOptimizerIntegrationTest.c src/RuneOptimizer/*/*.c
-TESTE_FILES=src/test/RuneOptimizerE2ETest.c src/RuneOptimizer/*/*.c
+FILES=src/cli/runeoptimizer/runeoptimizer.c src/cli/runeoptimizer/*/*.c
+TESTU_FILES=src/cli/test/runeoptimizer_unit_test.c src/cli/runeoptimizer/*/*.c
+TESTI_FILES=src/cli/test/runeoptimizer_integration_test.c src/cli/runeoptimizer/*/*.c
+TESTE_FILES=src/cli/test/runeoptimizer_e2e_test.c src/cli/runeoptimizer/*/*.c
 
 
 # Compiler
 # Compiler
 CC=gcc
 CC=gcc
@@ -62,13 +62,13 @@ TESTE_OUT=$(OUT_DIR)$(TESTE_OUT_NAME)$(OUT_EXT)
 
 
 # Instalation directories
 # Instalation directories
 INSTALL_CLI_SRC=$(OUT)
 INSTALL_CLI_SRC=$(OUT)
-INSTALL_GUI_SRC=RuneOptimizerGUI
+INSTALL_GUI_SRC=src/gui/runeoptimizer_gui
 INSTALL_DIR_BASE=/usr/share/runeoptimizer/
 INSTALL_DIR_BASE=/usr/share/runeoptimizer/
 INSTALL_DIR_EXEC=/usr/local/bin/
 INSTALL_DIR_EXEC=/usr/local/bin/
 INSTALL_CLI_EXEC_SRC=$(INSTALL_DIR_BASE)$(OUT_NAME)$(OUT_EXT)
 INSTALL_CLI_EXEC_SRC=$(INSTALL_DIR_BASE)$(OUT_NAME)$(OUT_EXT)
-INSTALL_GUI_EXEC_SRC=$(INSTALL_DIR_BASE)$(INSTALL_GUI_SRC)$(OUT_EXT)/RuneOptimizer.py
+INSTALL_GUI_EXEC_SRC=$(INSTALL_DIR_BASE)runeoptimizer_gui/runeoptimizer_gui.py
 INSTALL_CLI_EXEC_DST=$(INSTALL_DIR_EXEC)$(OUT_NAME)$(OUT_EXT)
 INSTALL_CLI_EXEC_DST=$(INSTALL_DIR_EXEC)$(OUT_NAME)$(OUT_EXT)
-INSTALL_GUI_EXEC_DST=$(INSTALL_DIR_EXEC)$(OUT_NAME)-gui$(OUT_EXT)
+INSTALL_GUI_EXEC_DST=$(INSTALL_DIR_EXEC)$(OUT_NAME)_gui$(OUT_EXT)
 
 
 CMD_DOC=doxygen
 CMD_DOC=doxygen
 CMD_RM=$(OS_RM)
 CMD_RM=$(OS_RM)

+ 1 - 1
README.md

@@ -53,7 +53,7 @@ Well, this was painfull to get right
     (Credit for this part and many thanks to http://source.online.free.fr/Windows_HowToCompileSQLite)
     (Credit for this part and many thanks to http://source.online.free.fr/Windows_HowToCompileSQLite)
     Download https://www.sqlite.org/2022/sqlite-amalgamation-3370200.zip . It contains four files:
     Download https://www.sqlite.org/2022/sqlite-amalgamation-3370200.zip . It contains four files:
 
 
-    * shell.c 
+    * shell.c
     * sqlite3.c
     * sqlite3.c
     * sqlite3.h
     * sqlite3.h
     * sqlite3ext.h
     * sqlite3ext.h

+ 0 - 195
RuneOptimizerGUI/RuneOptimizer.py

@@ -1,195 +0,0 @@
-#!/usr/bin/env python
-
-"""
-This file is part of RuneOptimizer.
-
-RuneOptimizer 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.
-
-RuneOptimizer 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
-RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
-
-"""
-
-import wx
-import wx.grid
-import wx.adv
-import sqlite3
-import math
-import subprocess
-from subprocess import Popen, PIPE
-import json
-import os
-import pipes
-import appdirs
-import multiprocessing
-from types import SimpleNamespace
-from time import sleep
-
-# Include files
-classes_to_include = [
-    '/gui/PanelUnits.py',
-    '/gui/PanelTeams.py',
-    '/gui/PanelOptimizer.py',
-    '/gui/PanelResults.py',
-    '/gui/PanelInfo.py',
-    '/gui/RuneOptimizerFrame.py',
-    '/gui/TabList.py',
-    '/gui/DialogConfirm.py',
-    '/gui/DialogUpdateJson.py',
-    '/gui/DialogNewTeam.py',
-    '/entity/RuneStat.py',
-    '/entity/Rune.py',
-    '/entity/StatSet.py',
-    '/entity/UnitTeam.py',
-    '/entity/Unit.py',
-    '/entity/Team.py',
-]
-for cls in classes_to_include:
-    exec(
-      compile(
-        source=open(os.path.dirname(os.path.realpath(__file__)) + cls).read(),
-        filename=os.path.dirname(os.path.realpath(__file__)) + cls,
-        mode='exec'
-      )
-    )
-
-# The path to the RuneOptimizer executable. Different in Windows.
-executable_path = \
-  os.path.dirname(os.path.realpath(__file__)) + '/../RuneOptimizer'
-if os.name == 'nt':
-    executable_path = \
-      os.path.dirname(os.path.realpath(__file__)) + '\..\RuneOptimizer.exe'
-
-# App information
-app_info = {
-    "name": "Rune Optimizer",
-    "app_version": "UNKNOWN",
-    "gui_version": "0.1-RC1",
-    "author": "Iñigo Valentin",
-    "author_mail": "i@inigovalentin.com",
-    "url": {
-        "Home page": "https://inigovalentin.com/projects/RuneOptimizer/",
-        "Source code": "https://github.com/InigoValentin/RuneOptimizer/"
-    }
-}
-
-# Global database connection
-conn = None
-
-RUNE_STATS = {
-    "HP": 1, "HP_P": 2, "ATK": 3, "ATK_P": 4, "DEF": 5, "DEF_P": 6,
-    "SPD": 8, "CRR": 9, "CRD": 10, "RES": 11, "ACC": 12
-}
-
-# Unit stat names, indexed with Com2Us values.
-STAT_NAMES = [
-  "NULL", "HP  ", "HP% ", "ATK ", "ATK%", "DEF ",
-  "DEF%", "NULL", "SPD ", "CRR ", "CRD ", "RES ", "ACC "
-]
-
-# Rune set names, indexed with Com2Us values.
-SET_NAMES = [
-  "NULL",    "ENERGY",  "GUARD",    "SWIFT",   "BLADE",    "RAGE",     #  0- 5
-  "FOCUS",   "ENDURE",  "FATAL",    "NULL",    "DESPAIR",  "VAMPIRE",  #  6-11
-  "NULL",    "VIOLENT", "NEMESIS",  "WILL",    "SHIELD",   "REVENGE",  # 12-17
-  "DESTROY", "FIGHT",   "DETERMIN", "ENHANCE", "ACCURACY", "TOLERANCE" # 18-23
-]
-
-# TODO: Work in progress...
-"""
-# Rune set names, indexed with Com2Us values.
-SET_SYMBOLS = [
-  "NULL",    "ENERGY",  "GUARD",    "SWIFT",   "ᚬ",    "ᛟ",     #  0- 5
-  "FOCUS",   "ENDURE",  "FATAL",    "NULL",    "ᛃ",  "VAMPIRE",  #  6-11
-  "NULL",    "ᛒ", "NEMESIS",  "WILL",    "SHIELD",   "ᛝ",  # 12-17
-  "DESTROY", "FIGHT",   "ᛗ", "ENHANCE", "ACCURACY", "TOLERANCE" # 18-23
-]
-
-SET_SYMBOLS_UNICODE = [
-  "NULL",    "ENERGY",  "GUARD",    "SWIFT",   "\u16AC",    "\u16DF", #  0- 5
-  "FOCUS",   "ENDURE",  "FATAL",    "NULL",    "\u16C3",  "VAMPIRE",  #  6-11
-  "NULL",    "\u16D2", "NEMESIS",  "WILL",    "SHIELD",   "\u16DD",  # 12-17
-  "DESTROY", "FIGHT",   "\u16D7", "ENHANCE", "ACCURACY", "TOLERANCE" # 18-23
-]
-"""
-
-QUALITY_NAMES = [
-  "NULL",
-  "NORMAL", "MAGIC", "RARE", "HERO", "LEGEND"
-  "NULL", "NULL", "NULL", "NULL", "NULL", "NULL",
-  "A.NORMAL", "A.MAGIC", "A.RARE", "A.HERO",  "A.LEGEND"
-]
-
-units = {}
-teams = {}
-
-def reload_units():
-    # Get all the units, sorted by priority desc
-    global units
-    global conn
-    units = {}
-    cursor = conn.execute("""
-      SELECT
-        id,
-        (
-          SELECT sum(priority)
-          FROM teams
-          WHERE id IN (SELECT team FROM units_teams WHERE unit = units.id)
-        ) AS prio
-      FROM units
-      ORDER BY prio DESC;
-    """)
-    rows = cursor.fetchall()
-    for row in rows:
-        units[str(row[0])] = Unit(str(row[0]))
-
-def reload_teams():
-    # Get all the teams, sorted by priority desc
-    global teams
-    global conn
-    teams = {}
-    cursor = conn.execute("SELECT id FROM teams ORDER BY priority DESC;")
-    rows = cursor.fetchall()
-    for row in rows:
-        teams[str(row[0])] = Team(str(row[0]))
-
-if __name__ == '__main__':
-    """
-    Main function.
-
-    Conects to the database and shows the initial frame.
-    """
-
-    #Get CLI app version
-    # TODO: Change executable name for windows
-    #if os.name == 'nt':
-    app_info["app_version"] = subprocess.check_output(
-      ['RuneOptimizer', 'version']
-    )
-
-    # TODO: Test for executable to exist
-    # TODO: Test for database to exist
-
-    # Open the database
-    conn = sqlite3.connect(
-      appdirs.user_data_dir("RuneOptimizer") + "/data.sqlite"
-    )
-    
-    reload_units()
-    reload_teams()
-
-    # Start the GUI
-    app = wx.App()
-    frm = RuneOptimizerFrame(
-      parent=None, title='Rune Optimizer', pos=(100, 100), size=(1000, 650)
-    )
-    frm.Show()
-    app.MainLoop()

+ 1 - 1
doxygen-cli.conf

@@ -864,7 +864,7 @@ WARN_LOGFILE           =
 # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
 # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
 # Note: If this tag is empty the current directory is searched.
 # Note: If this tag is empty the current directory is searched.
 
 
-INPUT                  =src/
+INPUT                  =src/cli/runeoptimizer
 
 
 # This tag can be used to specify the character encoding of the source files
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses

+ 1 - 1
doxygen-gui.conf

@@ -864,7 +864,7 @@ WARN_LOGFILE           =
 # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
 # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
 # Note: If this tag is empty the current directory is searched.
 # Note: If this tag is empty the current directory is searched.
 
 
-INPUT                  =RuneOptimizerGUI/
+INPUT                  =src/gui/runeoptimizer_gui/
 
 
 # This tag can be used to specify the character encoding of the source files
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses

+ 1 - 1
src/RuneOptimizer/db/db.c → src/cli/runeoptimizer/db/db.c

@@ -30,7 +30,7 @@
 #include <string.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include <unistd.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../error/error.h"
 #include "db.h"
 #include "db.h"
 
 

+ 3 - 3
src/RuneOptimizer/db/db.h → src/cli/runeoptimizer/db/db.h

@@ -38,7 +38,7 @@ extern int db_close();
 
 
 /**
 /**
  * Opens the database connection.
  * Opens the database connection.
- * 
+ *
  * Sets up  and enables the global db connection {@link db}. On error, it prints
  * Sets up  and enables the global db connection {@link db}. On error, it prints
  * a description to stderr.
  * a description to stderr.
  *
  *
@@ -53,7 +53,7 @@ extern int db_open(char *path);
 
 
 /**
 /**
  * Executes a database query for a result.
  * Executes a database query for a result.
- * 
+ *
  * Uses the global database connection {@link db}. If the connection is not
  * Uses the global database connection {@link db}. If the connection is not
  * initializad, it calls {@link db_open}. On error, it prints a description to
  * initializad, it calls {@link db_open}. On error, it prints a description to
  * stderr.
  * stderr.
@@ -74,7 +74,7 @@ extern int db_query(sqlite3_stmt **stmt, char query[], char *parameters[]);
 
 
 /**
 /**
  * Executes a database statement.
  * Executes a database statement.
- * 
+ *
  * Uses the global database connection {@link db}. If the connection is not
  * Uses the global database connection {@link db}. If the connection is not
  * initializad, it calls {@link db_open}. On error, it prints a description to
  * initializad, it calls {@link db_open}. On error, it prints a description to
  * stderr.
  * stderr.

+ 0 - 0
src/RuneOptimizer/error/error.h → src/cli/runeoptimizer/error/error.h


+ 0 - 0
src/RuneOptimizer/gui/gui.c → src/cli/runeoptimizer/gui/gui.c


+ 0 - 0
src/RuneOptimizer/gui/gui.h → src/cli/runeoptimizer/gui/gui.h


+ 1 - 1
src/RuneOptimizer/help/help.c → src/cli/runeoptimizer/help/help.c

@@ -25,7 +25,7 @@
  */
  */
 
 
 #include <stdio.h>
 #include <stdio.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "help.h"
 #include "help.h"
 
 
 void help(){
 void help(){

+ 0 - 0
src/RuneOptimizer/help/help.h → src/cli/runeoptimizer/help/help.h


+ 91 - 53
src/RuneOptimizer/optimize/optimize.c → src/cli/runeoptimizer/optimize/optimize.c

@@ -31,7 +31,8 @@
 #include <sqlite3.h>
 #include <sqlite3.h>
 #include <math.h>
 #include <math.h>
 #include <pthread.h>
 #include <pthread.h>
-#include "../RuneOptimizer.h"
+#include <signal.h>
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "../db/db.h"
 #include "optimize.h"
 #include "optimize.h"
@@ -310,7 +311,7 @@ static void optimize_sort_results(Result results[MAX_RESULTS], int total);
  * in {@link error.h} if there were problems.
  * in {@link error.h} if there were problems.
  * @todo Maybe I can bring that width down to 80...
  * @todo Maybe I can bring that width down to 80...
  */
  */
-int optimize_print_result(struct Unit *unit, Result *result);
+static int optimize_print_result(struct Unit *unit, Result *result);
 
 
 /**
 /**
  * Prepares and prints the output for the GUI.
  * Prepares and prints the output for the GUI.
@@ -319,15 +320,41 @@ int optimize_print_result(struct Unit *unit, Result *result);
  *
  *
  * @param[in] results All results.
  * @param[in] results All results.
  * @param[in] result_count Number of results.
  * @param[in] result_count Number of results.
+ * @param[in] level Rune requested level, as indicated with the "--level"
+ * argument.
  * @return {@link SUCCESS} if all the runes could be fetch, or an error defined
  * @return {@link SUCCESS} if all the runes could be fetch, or an error defined
  * in {@link error.h} if there were problems.
  * in {@link error.h} if there were problems.
  */
  */
-extern int optimize_print_gui(Result results[MAX_RESULTS], int result_count);
+static int optimize_print_gui(
+  Result results[MAX_RESULTS], int result_count, int level
+);
+
+/**
+ * Handles received signals.
+ *
+ * For SIGUSR1, it prints the current progress.
+ *
+ * @param[in] sig Signal code.
+ */
+static void optimize_handle_signal(int sig);
+
+/**
+ * The max number of combinations to test by the optimizer.
+ */
+unsigned long long max_combinations = 0;
+
+/**
+ * The number of combinations already tested by the optimizer.
+ */
+unsigned long long progress_combinations = 0;
 
 
 extern int optimize(int argc, char *argv[]){
 extern int optimize(int argc, char *argv[]){
 
 
     int status = SUCCESS;
     int status = SUCCESS;
 
 
+    // Set signal handler
+    signal(SIGUSR1, optimize_handle_signal);
+
     // Create custom argument array
     // Create custom argument array
     // If I don't do this, in the function optimize_parse_arguments,
     // If I don't do this, in the function optimize_parse_arguments,
     // every 7th (index 6, index 15...) are missing. NULL.
     // every 7th (index 6, index 15...) are missing. NULL.
@@ -396,7 +423,7 @@ extern int optimize(int argc, char *argv[]){
     }
     }
 
 
     // Precalculate how many combinations will have to be tested.
     // Precalculate how many combinations will have to be tested.
-    unsigned long long max_combinations =
+    max_combinations =
       (unsigned long long) rune_count[1] *
       (unsigned long long) rune_count[1] *
       (unsigned long long) rune_count[2] *
       (unsigned long long) rune_count[2] *
       (unsigned long long) rune_count[3] *
       (unsigned long long) rune_count[3] *
@@ -415,10 +442,6 @@ extern int optimize(int argc, char *argv[]){
     if (options.gui == FALSE){
     if (options.gui == FALSE){
         printf("\n\n__Optimization progress___________________________\n");
         printf("\n\n__Optimization progress___________________________\n");
     }
     }
-    else{
-        printf("Total combinations: %llu\n", max_combinations);
-        fflush(stdout);
-    }
 
 
     Optimizer_Data opt_data[options.threads];
     Optimizer_Data opt_data[options.threads];
     for (int t = 0; t < options.threads; t++){
     for (int t = 0; t < options.threads; t++){
@@ -475,7 +498,7 @@ extern int optimize(int argc, char *argv[]){
         }
         }
         else{
         else{
             // Create the output
             // Create the output
-            optimize_print_gui(results, result_count);
+            optimize_print_gui(results, result_count, options.level);
         }
         }
     }
     }
     else{
     else{
@@ -1490,48 +1513,48 @@ static void optimize_print_summary(
     printf("\n");
     printf("\n");
     printf(" -----------------------------------------\n");
     printf(" -----------------------------------------\n");
     printf(
     printf(
-      " | HP:  | %*d  | %*d  | %*d  |    ", 7,
+      " | HP:  | %*u  | %*u  | %*u  |    ", 7,
       unit->base_hp, 7, unit->current_hp, 7, options->min_stats->hp
       unit->base_hp, 7, unit->current_hp, 7, options->min_stats->hp
     );
     );
     if (options->level == 0) printf("Using runes at their current level\n");
     if (options->level == 0) printf("Using runes at their current level\n");
     else printf("Considering runes at level %d\n", options->level);
     else printf("Considering runes at level %d\n", options->level);
     printf(
     printf(
-      " | ATK: | %*d  | %*d  | %*d  |\n", 7,
+      " | ATK: | %*u  | %*u  | %*u  |\n", 7,
       unit->base_atk, 7, unit->current_atk, 7, options->min_stats->atk
       unit->base_atk, 7, unit->current_atk, 7, options->min_stats->atk
     );
     );
     printf(
     printf(
-      " | DEF: | %*d  | %*d  | %*d  |    Runes considered by slot:\n",
+      " | DEF: | %*u  | %*u  | %*u  |    Runes considered by slot:\n",
       7, unit->base_def, 7, unit->current_def, 7, options->min_stats->def
       7, unit->base_def, 7, unit->current_def, 7, options->min_stats->def
     );
     );
     printf(
     printf(
-      " | SPD: | %*d  | %*d  | %*d  |      1:%*d    2:%*d    3:%*d\n",
+      " | SPD: | %*u  | %*u  | %*u  |      1:%*u    2:%*u    3:%*u\n",
       7, unit->base_spd, 7, unit->current_spd, 7, options->min_stats->spd,
       7, unit->base_spd, 7, unit->current_spd, 7, options->min_stats->spd,
       3, rune_count[1], 3, rune_count[2], 3, rune_count[3]
       3, rune_count[1], 3, rune_count[2], 3, rune_count[3]
     );
     );
     printf(
     printf(
-      " | CRR: | %*d%% | %*d%% | %*d%% |      4:%*d    5:%*d    6:%*d\n",
+      " | CRR: | %*u%% | %*u%% | %*u%% |      4:%*u    5:%*u    6:%*u\n",
       7, unit->base_crr, 7, unit->current_crr, 7, options->min_stats->crr,
       7, unit->base_crr, 7, unit->current_crr, 7, options->min_stats->crr,
       3, rune_count[4], 3, rune_count[5], 3, rune_count[6]
       3, rune_count[4], 3, rune_count[5], 3, rune_count[6]
     );
     );
     printf(
     printf(
-      " | CRD: | %*d%% | %*d%% | %*d%% |\n",
+      " | CRD: | %*u%% | %*u%% | %*u%% |\n",
       7, unit->base_crd, 7, unit->current_crd, 7, options->min_stats->crd
       7, unit->base_crd, 7, unit->current_crd, 7, options->min_stats->crd
     );
     );
     printf(
     printf(
-      " | RES: | %*d%% | %*d%% | %*d%% |    Total combinations: %llu\n",
+      " | RES: | %*u%% | %*u%% | %*u%% |    Total combinations: %llu\n",
       7, unit->base_res, 7, unit->current_res, 7, options->min_stats->res,
       7, unit->base_res, 7, unit->current_res, 7, options->min_stats->res,
       max_combinations
       max_combinations
     );
     );
     printf(
     printf(
-      " | ACC: | %*d%% | %*d%% | %*d%% |\n",
+      " | ACC: | %*u%% | %*u%% | %*u%% |\n",
       7, unit->base_acc, 7, unit->current_acc, 7, options->min_stats->acc
       7, unit->base_acc, 7, unit->current_acc, 7, options->min_stats->acc
     );
     );
     printf(
     printf(
-      " | EHP: | %*d  | %*d  | %*d  |\n",
+      " | EHP: | %*u  | %*u  | %*u  |\n",
       7, unit->base_ehp, 7, unit->current_ehp, 7, options->min_stats->ehp
       7, unit->base_ehp, 7, unit->current_ehp, 7, options->min_stats->ehp
     );
     );
     printf(
     printf(
-      " | DMG: | %*d  | %*d  | %*d  |\n",
+      " | DMG: | %*u  | %*u  | %*u  |\n",
       7, unit->base_dmg, 7, unit->current_dmg, 7, options->min_stats->dmg
       7, unit->base_dmg, 7, unit->current_dmg, 7, options->min_stats->dmg
     );
     );
     printf(" -----------------------------------------\n");
     printf(" -----------------------------------------\n");
@@ -1564,19 +1587,13 @@ static void *optimize_thread(void *data){
           (unsigned long)((opt_data->max_combinations) / 50)
           (unsigned long)((opt_data->max_combinations) / 50)
           == 0
           == 0
         ){
         ){
+            //progress_combinations += tested_combinations;
             if (opt_data->options.gui == FALSE){
             if (opt_data->options.gui == FALSE){
                 printf("#");
                 printf("#");
                 //printf("%d", opt_data->thread_id);
                 //printf("%d", opt_data->thread_id);
+                // Line buffered! need to flush after every char.
+                fflush(stdout);
             }
             }
-            else{
-                // For GUI flushing, newlines are required
-                printf(
-                  "%lu\n",
-                  (unsigned long)( (tested_combinations + 1) /
-                  (unsigned long)(opt_data->max_combinations / 20)));
-            }
-            // Line buffered! need to flush after every char.
-            fflush(stdout);
         }
         }
 
 
         // Calculate rune sets at current indexes.
         // Calculate rune sets at current indexes.
@@ -1793,6 +1810,7 @@ static void *optimize_thread(void *data){
 
 
         // Loop control. Rotate the reels 'right to left'
         // Loop control. Rotate the reels 'right to left'
         tested_combinations ++;
         tested_combinations ++;
+        progress_combinations ++;
         index[6] ++;
         index[6] ++;
         if (index[6] == opt_data->count[6]){
         if (index[6] == opt_data->count[6]){
             index[6] = 0;
             index[6] = 0;
@@ -1873,16 +1891,16 @@ int optimize_print_result(struct Unit *unit, Result *result){
     printf(" ------------------------------\n");
     printf(" ------------------------------\n");
     printf(" | STAT | CURR.    | NEW      |\n");
     printf(" | STAT | CURR.    | NEW      |\n");
     printf(" ------------------------------\n");
     printf(" ------------------------------\n");
-    printf(" | HP:  | %*d  | %*d  |\n", 7, unit->current_hp, 7, result->stats.hp);
-    printf(" | ATK: | %*d  | %*d  |\n", 7, unit->current_atk, 7, result->stats.atk);
-    printf(" | DEF: | %*d  | %*d  |\n", 7, unit->current_def, 7, result->stats.def);
-    printf(" | SPD: | %*d  | %*d  |\n", 7, unit->current_spd, 7, result->stats.spd);
-    printf(" | CRR: | %*d%% | %*d%% |\n", 7, unit->current_crr, 7, result->stats.crr);
-    printf(" | CRD: | %*d%% | %*d%% |\n", 7, unit->current_crd, 7, result->stats.crd);
-    printf(" | RES: | %*d%% | %*d%% |\n", 7, unit->current_res, 7, result->stats.res);
-    printf(" | ACC: | %*d%% | %*d%% |\n", 7, unit->current_acc, 7, result->stats.acc);
-    printf(" | EHP: | %*d  | %*d  |\n", 7, unit->current_ehp, 7, result->stats.ehp);
-    printf(" | DMG: | %*d  | %*d  |\n", 7, unit->current_dmg, 7, result->stats.dmg);
+    printf(" | HP:  | %*u  | %*u  |\n", 7, unit->current_hp, 7, result->stats.hp);
+    printf(" | ATK: | %*u  | %*u  |\n", 7, unit->current_atk, 7, result->stats.atk);
+    printf(" | DEF: | %*u  | %*u  |\n", 7, unit->current_def, 7, result->stats.def);
+    printf(" | SPD: | %*u  | %*u  |\n", 7, unit->current_spd, 7, result->stats.spd);
+    printf(" | CRR: | %*u%% | %*u%% |\n", 7, unit->current_crr, 7, result->stats.crr);
+    printf(" | CRD: | %*u%% | %*u%% |\n", 7, unit->current_crd, 7, result->stats.crd);
+    printf(" | RES: | %*u%% | %*u%% |\n", 7, unit->current_res, 7, result->stats.res);
+    printf(" | ACC: | %*u%% | %*u%% |\n", 7, unit->current_acc, 7, result->stats.acc);
+    printf(" | EHP: | %*u  | %*u  |\n", 7, unit->current_ehp, 7, result->stats.ehp);
+    printf(" | DMG: | %*u  | %*u  |\n", 7, unit->current_dmg, 7, result->stats.dmg);
     printf(" ------------------------------\n");
     printf(" ------------------------------\n");
 
 
     // This bit may be hard to follow.
     // This bit may be hard to follow.
@@ -1984,7 +2002,7 @@ int optimize_print_result(struct Unit *unit, Result *result){
               parameters
               parameters
             );
             );
             int curr_slot = -1;
             int curr_slot = -1;
-            while (1 == 1){
+            while (1){
                 int status = sqlite3_step(stats_res);
                 int status = sqlite3_step(stats_res);
                 if (status == SQLITE_ROW){
                 if (status == SQLITE_ROW){
                     // Print empty lines for no-stats
                     // Print empty lines for no-stats
@@ -2136,37 +2154,40 @@ int optimize_print_result(struct Unit *unit, Result *result){
     return SUCCESS;
     return SUCCESS;
 }
 }
 
 
-int optimize_print_gui(Result results[MAX_RESULTS], int result_count){
+static int optimize_print_gui(
+  Result results[MAX_RESULTS], int result_count, int level
+){
     //Print for GUI
     //Print for GUI
     char tmp[300];
     char tmp[300];
     strcpy(tmp, "");
     strcpy(tmp, "");
     char json[300];
     char json[300];
-    printf("{\"result_count\":%d,\"results\":[", result_count);
+    printf("{\"result_count\":%d,", result_count);
+    printf("\"rune_level\":%d,", level);
+    printf("\"results\":[");
     int result_limit = 100;
     int result_limit = 100;
     for (int i = 0; i < result_count && i < result_limit; i++){
     for (int i = 0; i < result_count && i < result_limit; i++){
         //printf("RES LOOP %d", i);
         //printf("RES LOOP %d", i);
         sprintf(tmp, "{\"id\":%d,\"rating\":%d,", i, results[i].rating);
         sprintf(tmp, "{\"id\":%d,\"rating\":%d,", i, results[i].rating);
         strcpy(json, tmp);
         strcpy(json, tmp);
-        sprintf(tmp, "\"hp\":%d,", results[i].stats.hp);
+        sprintf(tmp, "\"hp\":%u,", results[i].stats.hp);
         strcat(json, tmp);
         strcat(json, tmp);
-        sprintf(tmp, "\"atk\":%d,", results[i].stats.atk);
+        sprintf(tmp, "\"atk\":%u,", results[i].stats.atk);
         strcat(json, tmp);
         strcat(json, tmp);
-        sprintf(tmp, "\"dfc\":%d,", results[i].stats.def);
+        sprintf(tmp, "\"dfc\":%u,", results[i].stats.def);
         strcat(json, tmp);
         strcat(json, tmp);
-        sprintf(tmp, "\"spd\":%d,", results[i].stats.spd);
+        sprintf(tmp, "\"spd\":%u,", results[i].stats.spd);
         strcat(json, tmp);
         strcat(json, tmp);
-        sprintf(tmp, "\"crr\":%d,", results[i].stats.crr);
+        sprintf(tmp, "\"crr\":%u,", results[i].stats.crr);
         strcat(json, tmp);
         strcat(json, tmp);
-
-        sprintf(tmp, "\"crd\":%d,", results[i].stats.crd);
+        sprintf(tmp, "\"crd\":%u,", results[i].stats.crd);
         strcat(json, tmp);
         strcat(json, tmp);
-        sprintf(tmp, "\"res\":%d,", results[i].stats.res);
+        sprintf(tmp, "\"res\":%u,", results[i].stats.res);
         strcat(json, tmp);
         strcat(json, tmp);
-        sprintf(tmp, "\"acc\":%d,", results[i].stats.acc);
+        sprintf(tmp, "\"acc\":%u,", results[i].stats.acc);
         strcat(json, tmp);
         strcat(json, tmp);
-        sprintf(tmp, "\"ehp\":%d,", results[i].stats.ehp);
+        sprintf(tmp, "\"ehp\":%u,", results[i].stats.ehp);
         strcat(json, tmp);
         strcat(json, tmp);
-        sprintf(tmp, "\"dmg\":%d,", results[i].stats.dmg);
+        sprintf(tmp, "\"dmg\":%u,", results[i].stats.dmg);
         strcat(json, tmp);
         strcat(json, tmp);
         strcat(json, "\"runes\":[");
         strcat(json, "\"runes\":[");
         //printf("    A %s", json);
         //printf("    A %s", json);
@@ -2188,7 +2209,24 @@ int optimize_print_gui(Result results[MAX_RESULTS], int result_count){
         printf(json);
         printf(json);
     }
     }
 
 
-    // End and print
+    // End
     printf("]}\n");
     printf("]}\n");
     return SUCCESS;
     return SUCCESS;
 }
 }
+
+static void optimize_handle_signal(int sig){
+    if (sig == SIGUSR1){
+        unsigned int progress;
+        if (0 == max_combinations){
+            progress = 0;
+        }
+        else{
+            progress = progress_combinations * 100 / max_combinations;
+        }
+        printf(
+          "  Progress: %u%% (%llu / %llu)\n",
+          progress, progress_combinations, max_combinations
+        );
+        fflush(stdout);
+    }
+}

+ 1 - 1
src/RuneOptimizer/optimize/optimize.h → src/cli/runeoptimizer/optimize/optimize.h

@@ -26,7 +26,7 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 
 
 /**
 /**
  * Hard limit on the number of results
  * Hard limit on the number of results

+ 1 - 1
src/RuneOptimizer/player/player.c → src/cli/runeoptimizer/player/player.c

@@ -25,7 +25,7 @@
  */
  */
 
 
 #include <stdio.h>
 #include <stdio.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "../db/db.h"
 #include "player.h"
 #include "player.h"

+ 0 - 0
src/RuneOptimizer/player/player.h → src/cli/runeoptimizer/player/player.h


+ 4 - 4
src/RuneOptimizer/RuneOptimizer.c → src/cli/runeoptimizer/runeoptimizer.c

@@ -16,12 +16,12 @@
  */
  */
 
 
 /**
 /**
- * @file RuneOptimizer.c
+ * @file runeoptimizer.c
  *
  *
  * Implementation of the functions used by the gui command.
  * Implementation of the functions used by the gui command.
  *
  *
  * This file implements the main function, and the functions declared in
  * This file implements the main function, and the functions declared in
- * {@link RuneOptimizer.h}.
+ * {@link runeoptimizer.h}.
  */
  */
 
 
 #include <stdio.h>
 #include <stdio.h>
@@ -29,7 +29,7 @@
 #include <string.h>
 #include <string.h>
 #include <sqlite3.h>
 #include <sqlite3.h>
 #include <math.h>
 #include <math.h>
-#include "RuneOptimizer.h"
+#include "runeoptimizer.h"
 #include "error/error.h"
 #include "error/error.h"
 #include "db/db.h"
 #include "db/db.h"
 #include "help/help.h"
 #include "help/help.h"
@@ -289,6 +289,6 @@ int main(int argc, char *argv[]){
         fprintf(stderr, "Invalid command specified: %s\n", argv[1]);
         fprintf(stderr, "Invalid command specified: %s\n", argv[1]);
         return(ERROR_INPUT_INVALID_COMMAND);
         return(ERROR_INPUT_INVALID_COMMAND);
     }
     }
-    
+
     return result;
     return result;
 }
 }

+ 1 - 1
src/RuneOptimizer/RuneOptimizer.h → src/cli/runeoptimizer/runeoptimizer.h

@@ -16,7 +16,7 @@
  */
  */
 
 
 /**
 /**
- * @file RuneOptimizer.h
+ * @file runeoptimizer.h
  *
  *
  * Declarations of the functions and data types used across the application.
  * Declarations of the functions and data types used across the application.
  *
  *

+ 1 - 1
src/RuneOptimizer/team/team.c → src/cli/runeoptimizer/team/team.c

@@ -25,7 +25,7 @@
 
 
 #include <stdio.h>
 #include <stdio.h>
 #include <string.h>
 #include <string.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../error/error.h"
 #include "team.h"
 #include "team.h"
 
 

+ 3 - 3
src/RuneOptimizer/team/team.h → src/cli/runeoptimizer/team/team.h

@@ -28,7 +28,7 @@
 
 
 /**
 /**
  * Adds a unit to a team.
  * Adds a unit to a team.
- * 
+ *
  * Inserts a row in the table 'units_teams'. In case of error, it will print a
  * Inserts a row in the table 'units_teams'. In case of error, it will print a
  * description to stderr.
  * description to stderr.
  *
  *
@@ -43,7 +43,7 @@ extern int team_add_unit(int argc, char *argv[]);
 
 
 /**
 /**
  * Creates a team.
  * Creates a team.
- * 
+ *
  * Saves it to the database table 'teams 'with the provided name and
  * Saves it to the database table 'teams 'with the provided name and
  * (optionally) priority. It will have no units in 'units_teams'. In case of
  * (optionally) priority. It will have no units in 'units_teams'. In case of
  * error, it will print a description to stderr.
  * error, it will print a description to stderr.
@@ -60,7 +60,7 @@ extern int team_create(int argc, char *argv[]);
 
 
 /**
 /**
  * Deletes a team.
  * Deletes a team.
- * 
+ *
  * It deletes the row from the table 'teams', and any related row in the table
  * It deletes the row from the table 'teams', and any related row in the table
  * 'units_teams'. In case of error, it will print a
  * 'units_teams'. In case of error, it will print a
  * description to stderr.
  * description to stderr.

+ 1 - 1
src/RuneOptimizer/team/team_add_unit.c → src/cli/runeoptimizer/team/team_add_unit.c

@@ -26,7 +26,7 @@
  */
  */
 
 
 #include <stdio.h>
 #include <stdio.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "../db/db.h"
 #include "team.h"
 #include "team.h"

+ 2 - 2
src/RuneOptimizer/team/team_create.c → src/cli/runeoptimizer/team/team_create.c

@@ -28,7 +28,7 @@
 #include <stdio.h>
 #include <stdio.h>
 #include <string.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stdlib.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "../db/db.h"
 #include "team.h"
 #include "team.h"
@@ -78,7 +78,7 @@ extern int team_create(int argc, char *argv[]){
         fprintf(stderr, "Unable to get id for the team\n");
         fprintf(stderr, "Unable to get id for the team\n");
         return(ERROR_DB_NEW_TEAM_ID);
         return(ERROR_DB_NEW_TEAM_ID);
     }
     }
-    
+
     char priority_c[2];
     char priority_c[2];
     char id_c[4];
     char id_c[4];
     sprintf(id_c, "%d", new_id);
     sprintf(id_c, "%d", new_id);

+ 3 - 3
src/RuneOptimizer/team/team_delete.c → src/cli/runeoptimizer/team/team_delete.c

@@ -26,7 +26,7 @@
  */
  */
 
 
 #include <stdio.h>
 #include <stdio.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "../db/db.h"
 #include "team.h"
 #include "team.h"
@@ -50,8 +50,8 @@ extern int team_delete(int argc, char *argv[]){
         );
         );
         return(ERROR_INPUT_TEAM_DELETE_ARGUMENT_COUNT);
         return(ERROR_INPUT_TEAM_DELETE_ARGUMENT_COUNT);
     }
     }
-    
-    
+
+
     // Verify that the team exists
     // Verify that the team exists
     int validation = team_delete_validate(argv[0]);
     int validation = team_delete_validate(argv[0]);
     if (SUCCESS != validation){
     if (SUCCESS != validation){

+ 2 - 2
src/RuneOptimizer/team/team_list.c → src/cli/runeoptimizer/team/team_list.c

@@ -27,7 +27,7 @@
 #include <stdio.h>
 #include <stdio.h>
 #include <string.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stdlib.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "../db/db.h"
 #include "team.h"
 #include "team.h"
@@ -57,7 +57,7 @@ extern int team_list(int argc, char *argv[]){
     printf(" --------------------------------------------------\n");
     printf(" --------------------------------------------------\n");
     printf(" | ID   | Name                             | Prio |\n");
     printf(" | ID   | Name                             | Prio |\n");
     printf(" --------------------------------------------------\n");
     printf(" --------------------------------------------------\n");
-    while (1 == 1){
+    while (1){
         int status = sqlite3_step(stmt_teams);
         int status = sqlite3_step(stmt_teams);
         if (status == SQLITE_ROW){
         if (status == SQLITE_ROW){
             printf(
             printf(

+ 1 - 1
src/RuneOptimizer/team/team_remove_unit.c → src/cli/runeoptimizer/team/team_remove_unit.c

@@ -26,7 +26,7 @@
  */
  */
 
 
 #include <stdio.h>
 #include <stdio.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "../db/db.h"
 #include "team.h"
 #include "team.h"

+ 4 - 4
src/RuneOptimizer/unit/unit.c → src/cli/runeoptimizer/unit/unit.c

@@ -26,7 +26,7 @@
 
 
 #include <stdio.h>
 #include <stdio.h>
 #include <string.h>
 #include <string.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "../db/db.h"
 #include "unit.h"
 #include "unit.h"
@@ -72,10 +72,10 @@ static int unit_list(char *search){
       parameters
       parameters
     );
     );
     int units_found = 0;
     int units_found = 0;
-    int unit_has_runes = FALSE;
     char tmp[32];
     char tmp[32];
     while (SQLITE_ROW == sqlite3_step(stmt_units)){
     while (SQLITE_ROW == sqlite3_step(stmt_units)){
         char unit_name[14];
         char unit_name[14];
+        int unit_has_runes = FALSE;
 
 
         // Print a separator between units
         // Print a separator between units
         if (units_found > 0){
         if (units_found > 0){
@@ -144,7 +144,7 @@ static int unit_list(char *search){
           6, sqlite3_column_int(stmt_units, 20)
           6, sqlite3_column_int(stmt_units, 20)
         );
         );
         printf(
         printf(
-          "                 | EHP: | %*d  | %*d  |\n",
+          "                 | EHP: | %*u  | %*u  |\n",
           6,
           6,
           calculate_ehp(
           calculate_ehp(
             sqlite3_column_int(stmt_units, 5),
             sqlite3_column_int(stmt_units, 5),
@@ -157,7 +157,7 @@ static int unit_list(char *search){
           )
           )
         );
         );
         printf(
         printf(
-          "                 | DMG: | %*d  | %*d  |\n",
+          "                 | DMG: | %*u  | %*u  |\n",
           6,
           6,
           calculate_dmg(
           calculate_dmg(
             sqlite3_column_int(stmt_units, 6),
             sqlite3_column_int(stmt_units, 6),

+ 0 - 0
src/RuneOptimizer/unit/unit.h → src/cli/runeoptimizer/unit/unit.h


+ 1 - 1
src/RuneOptimizer/update/update.c → src/cli/runeoptimizer/update/update.c

@@ -25,7 +25,7 @@
 
 
 #include <stdio.h>
 #include <stdio.h>
 #include <string.h>
 #include <string.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../error/error.h"
 #include "update.h"
 #include "update.h"
 
 

+ 5 - 5
src/RuneOptimizer/update/update.h → src/cli/runeoptimizer/update/update.h

@@ -26,7 +26,7 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 
 
 /**
 /**
  * Max substats in a rune.
  * Max substats in a rune.
@@ -658,7 +658,7 @@ extern const int MAIN_STATS_VALUES[DIFFERENT_STATS][RUNE_MAX_STARS + 1][2];
 
 
 /**
 /**
  * Calculates the current stats for units with runes in the database.
  * Calculates the current stats for units with runes in the database.
- * 
+ *
  * It counts the bonus stats provided by runes and sets, and updates the
  * It counts the bonus stats provided by runes and sets, and updates the
  * current_* columns in the database for every unit with at least one rune
  * current_* columns in the database for every unit with at least one rune
  * assigned.
  * assigned.
@@ -670,7 +670,7 @@ extern int update_current_stats();
 
 
 /**
 /**
  * Recreates the tables in the database.
  * Recreates the tables in the database.
- * 
+ *
  * It does not just delete the data, but it actualy drops and recreats the
  * It does not just delete the data, but it actualy drops and recreats the
  * tables. By default, itdoes this to all tables except 'teams' and
  * tables. By default, itdoes this to all tables except 'teams' and
  * 'units_teams'.
  * 'units_teams'.
@@ -684,7 +684,7 @@ extern int update_db_tables(unsigned char clear_teams);
 
 
 /**
 /**
  * Gets a monster name from it's ID.
  * Gets a monster name from it's ID.
- * 
+ *
  * If the monster is unawakened, the name will be in the format
  * If the monster is unawakened, the name will be in the format
  * 'name (element)'. If the monster is second awakened, the name will be
  * 'name (element)'. If the monster is second awakened, the name will be
  * 'name (2A)'. For the rest of cases, it will be just 'name'. In any case, the
  * 'name (2A)'. For the rest of cases, it will be just 'name'. In any case, the
@@ -698,7 +698,7 @@ extern void update_get_monster_name(int id, char name[UNIT_NAME_LEN]);
 
 
 /**
 /**
  * Updaes the data in the table 'info'.
  * Updaes the data in the table 'info'.
- * 
+ *
  * @param[in] id Player ID.
  * @param[in] id Player ID.
  * @param[in] name Player name.
  * @param[in] name Player name.
  * @param[in] level Player level.
  * @param[in] level Player level.

+ 9 - 9
src/RuneOptimizer/update/update_current_stats.c → src/cli/runeoptimizer/update/update_current_stats.c

@@ -28,7 +28,7 @@
 #include <string.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stdlib.h>
 #include <sqlite3.h>
 #include <sqlite3.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "../db/db.h"
 #include "update.h"
 #include "update.h"
@@ -171,14 +171,14 @@ extern int update_current_stats(){
         for (int i = 0; i < 9; i ++){
         for (int i = 0; i < 9; i ++){
             parameters_update[i] = malloc(5);
             parameters_update[i] = malloc(5);
         }
         }
-        sprintf(parameters_update[0], "%d", unit.current_hp);
-        sprintf(parameters_update[1], "%d", unit.current_atk);
-        sprintf(parameters_update[2], "%d", unit.current_def);
-        sprintf(parameters_update[3], "%d", unit.current_spd);
-        sprintf(parameters_update[4], "%d", unit.current_crr);
-        sprintf(parameters_update[5], "%d", unit.current_crd);
-        sprintf(parameters_update[6], "%d", unit.current_res);
-        sprintf(parameters_update[7], "%d", unit.current_acc);
+        sprintf(parameters_update[0], "%u", unit.current_hp);
+        sprintf(parameters_update[1], "%u", unit.current_atk);
+        sprintf(parameters_update[2], "%u", unit.current_def);
+        sprintf(parameters_update[3], "%u", unit.current_spd);
+        sprintf(parameters_update[4], "%u", unit.current_crr);
+        sprintf(parameters_update[5], "%u", unit.current_crd);
+        sprintf(parameters_update[6], "%u", unit.current_res);
+        sprintf(parameters_update[7], "%u", unit.current_acc);
         parameters_update[8] = (char *) unit.id;
         parameters_update[8] = (char *) unit.id;
         if (
         if (
           SUCCESS !=
           SUCCESS !=

+ 1 - 1
src/RuneOptimizer/update/update_db_tables.c → src/cli/runeoptimizer/update/update_db_tables.c

@@ -26,7 +26,7 @@
 
 
 #include <stdio.h>
 #include <stdio.h>
 #include <sqlite3.h>
 #include <sqlite3.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "../db/db.h"
 
 

+ 5 - 9
src/RuneOptimizer/update/update_efficiency.c → src/cli/runeoptimizer/update/update_efficiency.c

@@ -28,7 +28,7 @@
 #include <sqlite3.h>
 #include <sqlite3.h>
 #include "../error/error.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "../db/db.h"
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 
 
 extern int update_efficiency(char *id, float *efficiency, float *max_efficiency){
 extern int update_efficiency(char *id, float *efficiency, float *max_efficiency){
     *efficiency = 0.0f;
     *efficiency = 0.0f;
@@ -55,15 +55,11 @@ extern int update_efficiency(char *id, float *efficiency, float *max_efficiency)
 
 
     // Calculate efficiency for each slot.
     // Calculate efficiency for each slot.
     // Main slot is excluded from the calculation.
     // Main slot is excluded from the calculation.
-    int slot = -1;
-    int stat = -1;
-    int value = -1;
-    int max_roll_value = -1;
     while (SQLITE_ROW == sqlite3_step(stmt_stats)){
     while (SQLITE_ROW == sqlite3_step(stmt_stats)){
-        slot = sqlite3_column_int(stmt_stats, 0);
-        stat = sqlite3_column_int(stmt_stats, 1);
-        value = sqlite3_column_int(stmt_stats, 2);
-        max_roll_value = STAT_ROLL_MAX[stat][stars];
+        int slot = sqlite3_column_int(stmt_stats, 0);
+        int stat = sqlite3_column_int(stmt_stats, 1);
+        int value = sqlite3_column_int(stmt_stats, 2);
+        int max_roll_value = STAT_ROLL_MAX[stat][stars];
         eff[slot] += (((float) value) / ((float) max_roll_value));
         eff[slot] += (((float) value) / ((float) max_roll_value));
     }
     }
     sqlite3_finalize(stmt_stats);
     sqlite3_finalize(stmt_stats);

+ 2 - 2
src/RuneOptimizer/update/update_get_monster_name.c → src/cli/runeoptimizer/update/update_get_monster_name.c

@@ -25,10 +25,10 @@
  */
  */
 
 
 #include <string.h>
 #include <string.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 
 
 extern void update_get_monster_name(int id, char name[UNIT_NAME_LEN]){
 extern void update_get_monster_name(int id, char name[UNIT_NAME_LEN]){
-    
+
     /*
     /*
      * This list is generated from a SWDB database, with the query:
      * This list is generated from a SWDB database, with the query:
      * SELECT (
      * SELECT (

+ 3 - 2
src/RuneOptimizer/update/update_info.c → src/cli/runeoptimizer/update/update_info.c

@@ -26,11 +26,12 @@
 
 
 #include <stdio.h>
 #include <stdio.h>
 #include <sqlite3.h>
 #include <sqlite3.h>
+#include <string.h>
 #include <stdlib.h>
 #include <stdlib.h>
 #include <time.h>
 #include <time.h>
 #include "../error/error.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "../db/db.h"
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 
 
 extern int update_info(const char *id, const char *name, const char *level){
 extern int update_info(const char *id, const char *name, const char *level){
     time_t t = time(NULL);
     time_t t = time(NULL);
@@ -43,7 +44,7 @@ extern int update_info(const char *id, const char *name, const char *level){
       tm.tm_hour, tm.tm_min, tm.tm_sec
       tm.tm_hour, tm.tm_min, tm.tm_sec
     );
     );
     char *query_parameters[5];
     char *query_parameters[5];
-    for (int i = 1; i < 5; i ++) query_parameters[i] = malloc(5);
+    for (int i = 0; i < 5; i ++) query_parameters[i] = malloc(strlen(ts));
     sprintf(query_parameters[0], "%s", id);
     sprintf(query_parameters[0], "%s", id);
     sprintf(query_parameters[1], "%s", name);
     sprintf(query_parameters[1], "%s", name);
     sprintf(query_parameters[2], "%s", level);
     sprintf(query_parameters[2], "%s", level);

+ 68 - 69
src/RuneOptimizer/update/update_json.c → src/cli/runeoptimizer/update/update_json.c

@@ -29,7 +29,7 @@
 #include <string.h>
 #include <string.h>
 #include <sqlite3.h>
 #include <sqlite3.h>
 #include <json-c/json.h>
 #include <json-c/json.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "../db/db.h"
 #include "update.h"
 #include "update.h"
@@ -120,7 +120,7 @@ extern int update_json(
     int unit_count = json_object_array_length(unit_list);
     int unit_count = json_object_array_length(unit_list);
     for (int i = 0; i < unit_count; i++){
     for (int i = 0; i < unit_count; i++){
         idx = json_object_array_get_idx(unit_list, i);
         idx = json_object_array_get_idx(unit_list, i);
-        
+
         status = update_json_unit(
         status = update_json_unit(
           idx, six_stars, with_runes, &total_runes, &total_stats
           idx, six_stars, with_runes, &total_runes, &total_stats
         );
         );
@@ -137,11 +137,11 @@ extern int update_json(
       json_object_get_string(wizard_level)
       json_object_get_string(wizard_level)
     );
     );
     if (SUCCESS != status) return(status);
     if (SUCCESS != status) return(status);
-    
+
     // All parsed
     // All parsed
-    printf("%d units saved.\n", total_units);
-    printf("%d runes saved.\n", total_runes);
-    printf("%d rune stats saved.\n", total_stats);
+    printf("%u units saved.\n", total_units);
+    printf("%u runes saved.\n", total_runes);
+    printf("%u rune stats saved.\n", total_stats);
     printf("Player info updated\n");
     printf("Player info updated\n");
 
 
     return(SUCCESS);
     return(SUCCESS);
@@ -151,7 +151,7 @@ static int update_json_rune(json_object *rune_json, unsigned char *unit_id){
     int total_stats = 0;
     int total_stats = 0;
     DB_Rune rune;
     DB_Rune rune;
     char *query_parameters[43];
     char *query_parameters[43];
-    for (int i = 1; i < 43; i ++) query_parameters[i] = malloc(5);
+    for (int i = 1; i < 43; i ++) query_parameters[i] = malloc(35);
     // Rune ID
     // Rune ID
     json_object *rune_id = json_object_object_get(rune_json, "rune_id");
     json_object *rune_id = json_object_object_get(rune_json, "rune_id");
     strcpy((char *) rune.id, json_object_get_string(rune_id));
     strcpy((char *) rune.id, json_object_get_string(rune_id));
@@ -196,11 +196,11 @@ static int update_json_rune(json_object *rune_json, unsigned char *unit_id){
     json_object *sec_eff = json_object_object_get(rune_json, "sec_eff");
     json_object *sec_eff = json_object_object_get(rune_json, "sec_eff");
     // Loop stats
     // Loop stats
     int stat_count = json_object_array_length(sec_eff);
     int stat_count = json_object_array_length(sec_eff);
-    json_object *stat_json;
+
     rune.stat_count = 0;
     rune.stat_count = 0;
     for (int i = 0; i < stat_count && i < 4; i++){
     for (int i = 0; i < stat_count && i < 4; i++){
         rune.stat_count ++;
         rune.stat_count ++;
-        stat_json = json_object_array_get_idx(sec_eff, i);
+        json_object *stat_json = json_object_array_get_idx(sec_eff, i);
         json_object *stat = json_object_array_get_idx(stat_json, 0);
         json_object *stat = json_object_array_get_idx(stat_json, 0);
         rune.stats[i].stat = json_object_get_int(stat);
         rune.stats[i].stat = json_object_get_int(stat);
         json_object *value = json_object_array_get_idx(stat_json, 1);
         json_object *value = json_object_array_get_idx(stat_json, 1);
@@ -225,39 +225,39 @@ static int update_json_rune(json_object *rune_json, unsigned char *unit_id){
     sprintf(query_parameters[7], "%f", rune.efficiency);
     sprintf(query_parameters[7], "%f", rune.efficiency);
     sprintf(query_parameters[8], "%f", rune.max_efficiency);
     sprintf(query_parameters[8], "%f", rune.max_efficiency);
     sprintf(query_parameters[9], "%d", rune.main.stat);
     sprintf(query_parameters[9], "%d", rune.main.stat);
-    sprintf(query_parameters[10], "%d", rune.current_hp_percent);
-    sprintf(query_parameters[11], "%d", rune.current_atk_percent);
-    sprintf(query_parameters[12], "%d", rune.current_def_percent);
-    sprintf(query_parameters[13], "%d", rune.current_hp_flat);
-    sprintf(query_parameters[14], "%d", rune.current_atk_flat);
-    sprintf(query_parameters[15], "%d", rune.current_def_flat);
-    sprintf(query_parameters[16], "%d", rune.current_spd);
-    sprintf(query_parameters[17], "%d", rune.current_crr);
-    sprintf(query_parameters[18], "%d", rune.current_crd);
-    sprintf(query_parameters[19], "%d", rune.current_acc);
-    sprintf(query_parameters[20], "%d", rune.current_res);
-    sprintf(query_parameters[21], "%d", rune.lv12_hp_percent);
-    sprintf(query_parameters[22], "%d", rune.lv12_atk_percent);
-    sprintf(query_parameters[23], "%d", rune.lv12_def_percent);
-    sprintf(query_parameters[24], "%d", rune.lv12_hp_flat);
-    sprintf(query_parameters[25], "%d", rune.lv12_atk_flat);
-    sprintf(query_parameters[26], "%d", rune.lv12_def_flat);
-    sprintf(query_parameters[27], "%d", rune.lv12_spd);
-    sprintf(query_parameters[28], "%d", rune.lv12_crr);
-    sprintf(query_parameters[29], "%d", rune.lv12_crd);
-    sprintf(query_parameters[30], "%d", rune.lv12_acc);
-    sprintf(query_parameters[31], "%d", rune.lv12_res);
-    sprintf(query_parameters[32], "%d", rune.lv15_hp_percent);
-    sprintf(query_parameters[33], "%d", rune.lv15_atk_percent);
-    sprintf(query_parameters[34], "%d", rune.lv15_def_percent);
-    sprintf(query_parameters[35], "%d", rune.lv15_hp_flat);
-    sprintf(query_parameters[36], "%d", rune.lv15_atk_flat);
-    sprintf(query_parameters[37], "%d", rune.lv15_def_flat);
-    sprintf(query_parameters[38], "%d", rune.lv15_spd);
-    sprintf(query_parameters[39], "%d", rune.lv15_crr);
-    sprintf(query_parameters[40], "%d", rune.lv15_crd);
-    sprintf(query_parameters[41], "%d", rune.lv15_acc);
-    sprintf(query_parameters[42], "%d", rune.lv15_res);
+    sprintf(query_parameters[10], "%u", rune.current_hp_percent);
+    sprintf(query_parameters[11], "%u", rune.current_atk_percent);
+    sprintf(query_parameters[12], "%u", rune.current_def_percent);
+    sprintf(query_parameters[13], "%u", rune.current_hp_flat);
+    sprintf(query_parameters[14], "%u", rune.current_atk_flat);
+    sprintf(query_parameters[15], "%u", rune.current_def_flat);
+    sprintf(query_parameters[16], "%u", rune.current_spd);
+    sprintf(query_parameters[17], "%u", rune.current_crr);
+    sprintf(query_parameters[18], "%u", rune.current_crd);
+    sprintf(query_parameters[19], "%u", rune.current_acc);
+    sprintf(query_parameters[20], "%u", rune.current_res);
+    sprintf(query_parameters[21], "%u", rune.lv12_hp_percent);
+    sprintf(query_parameters[22], "%u", rune.lv12_atk_percent);
+    sprintf(query_parameters[23], "%u", rune.lv12_def_percent);
+    sprintf(query_parameters[24], "%u", rune.lv12_hp_flat);
+    sprintf(query_parameters[25], "%u", rune.lv12_atk_flat);
+    sprintf(query_parameters[26], "%u", rune.lv12_def_flat);
+    sprintf(query_parameters[27], "%u", rune.lv12_spd);
+    sprintf(query_parameters[28], "%u", rune.lv12_crr);
+    sprintf(query_parameters[29], "%u", rune.lv12_crd);
+    sprintf(query_parameters[30], "%u", rune.lv12_acc);
+    sprintf(query_parameters[31], "%u", rune.lv12_res);
+    sprintf(query_parameters[32], "%u", rune.lv15_hp_percent);
+    sprintf(query_parameters[33], "%u", rune.lv15_atk_percent);
+    sprintf(query_parameters[34], "%u", rune.lv15_def_percent);
+    sprintf(query_parameters[35], "%u", rune.lv15_hp_flat);
+    sprintf(query_parameters[36], "%u", rune.lv15_atk_flat);
+    sprintf(query_parameters[37], "%u", rune.lv15_def_flat);
+    sprintf(query_parameters[38], "%u", rune.lv15_spd);
+    sprintf(query_parameters[39], "%u", rune.lv15_crr);
+    sprintf(query_parameters[40], "%u", rune.lv15_crd);
+    sprintf(query_parameters[41], "%u", rune.lv15_acc);
+    sprintf(query_parameters[42], "%u", rune.lv15_res);
     if (
     if (
       SUCCESS !=
       SUCCESS !=
       db_execute(
       db_execute(
@@ -293,7 +293,7 @@ static int update_json_rune(json_object *rune_json, unsigned char *unit_id){
     //query_parameters[0] = rune.id; // No need, already has id
     //query_parameters[0] = rune.id; // No need, already has id
     query_parameters[1] = "-1"; // Main stat
     query_parameters[1] = "-1"; // Main stat
     sprintf(query_parameters[2], "%d", rune.main.stat);
     sprintf(query_parameters[2], "%d", rune.main.stat);
-    sprintf(query_parameters[3], "%d", rune.main.value);
+    sprintf(query_parameters[3], "%u", rune.main.value);
     query_parameters[4] = "0"; // Mains can't be grinded.
     query_parameters[4] = "0"; // Mains can't be grinded.
     query_parameters[5] = "0"; // Mains can't be enchanted.
     query_parameters[5] = "0"; // Mains can't be enchanted.
     if (
     if (
@@ -316,7 +316,7 @@ static int update_json_rune(json_object *rune_json, unsigned char *unit_id){
         //query_parameters[0] = rune.id; // No need, already has id
         //query_parameters[0] = rune.id; // No need, already has id
         query_parameters[1] = "0"; // Main stat
         query_parameters[1] = "0"; // Main stat
         sprintf(query_parameters[2], "%d", rune.innate.stat);
         sprintf(query_parameters[2], "%d", rune.innate.stat);
-        sprintf(query_parameters[3], "%d", rune.innate.value);
+        sprintf(query_parameters[3], "%u", rune.innate.value);
         query_parameters[4] = "0"; // Innates can't be grinded.
         query_parameters[4] = "0"; // Innates can't be grinded.
         query_parameters[5] = "0"; // Innates can't be enchanted.
         query_parameters[5] = "0"; // Innates can't be enchanted.
         if (
         if (
@@ -344,9 +344,9 @@ static int update_json_rune(json_object *rune_json, unsigned char *unit_id){
         sprintf(query_parameters[1], "%d", s + 1); // 1-4
         sprintf(query_parameters[1], "%d", s + 1); // 1-4
         query_parameters[2] = malloc(5);
         query_parameters[2] = malloc(5);
         sprintf(query_parameters[2], "%d", rune.stats[s].stat);
         sprintf(query_parameters[2], "%d", rune.stats[s].stat);
-        sprintf(query_parameters[3], "%d", rune.stats[s].value);
+        sprintf(query_parameters[3], "%u", rune.stats[s].value);
         query_parameters[4] = malloc(5);
         query_parameters[4] = malloc(5);
-        sprintf(query_parameters[4], "%d", rune.stats[s].grind);
+        sprintf(query_parameters[4], "%u", rune.stats[s].grind);
         query_parameters[5] = malloc(5);
         query_parameters[5] = malloc(5);
         sprintf(query_parameters[5], "%d", rune.stats[s].enchant);
         sprintf(query_parameters[5], "%d", rune.stats[s].enchant);
         if (
         if (
@@ -365,14 +365,15 @@ static int update_json_rune(json_object *rune_json, unsigned char *unit_id){
         }
         }
 
 
     }
     }
-
     // Now that everything is in the database, I can calculate efficiencies
     // Now that everything is in the database, I can calculate efficiencies
     float efficiency = 0.0f;
     float efficiency = 0.0f;
     float max_efficiency = 0.0f;
     float max_efficiency = 0.0f;
     update_efficiency(query_parameters[0], &efficiency, &max_efficiency);
     update_efficiency(query_parameters[0], &efficiency, &max_efficiency);
     strcpy(query_parameters[2], query_parameters[0]); //ID
     strcpy(query_parameters[2], query_parameters[0]); //ID
-    sprintf(query_parameters[0], "%6.2f", efficiency);
-    sprintf(query_parameters[1], "%6.2f", max_efficiency);
+    query_parameters[1] = malloc(35);
+    query_parameters[2] = malloc(35);
+    sprintf(query_parameters[0], "%3.2f", efficiency);
+    sprintf(query_parameters[1], "%3.2f", max_efficiency);
     if (
     if (
       SUCCESS !=
       SUCCESS !=
       db_execute(
       db_execute(
@@ -385,7 +386,6 @@ static int update_json_rune(json_object *rune_json, unsigned char *unit_id){
         );
         );
         return(-1);
         return(-1);
     }
     }
-
     return(total_stats);
     return(total_stats);
 }
 }
 
 
@@ -448,12 +448,11 @@ static int update_json_unit(
     // Parse unit runes
     // Parse unit runes
     json_object *unit_runes = json_object_object_get(unit_json, "runes");
     json_object *unit_runes = json_object_object_get(unit_json, "runes");
     int rune_count = json_object_array_length(unit_runes);
     int rune_count = json_object_array_length(unit_runes);
-    json_object *idx;
     for (int i = 0; i < rune_count; i++){
     for (int i = 0; i < rune_count; i++){
         unit_has_runes = TRUE;
         unit_has_runes = TRUE;
         // TODO: Test this: reference or value?
         // TODO: Test this: reference or value?
         total_runes ++;
         total_runes ++;
-        idx = json_object_array_get_idx(unit_runes, i);
+        json_object *idx = json_object_array_get_idx(unit_runes, i);
         int status = update_json_rune(idx, unit.id);
         int status = update_json_rune(idx, unit.id);
         if (status != -1) *total_stats += status;
         if (status != -1) *total_stats += status;
     }
     }
@@ -469,27 +468,27 @@ static int update_json_unit(
         query_parameters[1] = (char *) unit.name;
         query_parameters[1] = (char *) unit.name;
         sprintf(query_parameters[2], "%d", unit.stars);
         sprintf(query_parameters[2], "%d", unit.stars);
         sprintf(query_parameters[3], "%d", unit.level);
         sprintf(query_parameters[3], "%d", unit.level);
-        sprintf(query_parameters[4], "%d", unit.base_hp);
-        sprintf(query_parameters[5], "%d", unit.base_atk);
-        sprintf(query_parameters[6], "%d", unit.base_def);
-        sprintf(query_parameters[7], "%d", unit.base_spd);
-        sprintf(query_parameters[8], "%d", unit.base_crr);
-        sprintf(query_parameters[9], "%d", unit.base_crd);
-        sprintf(query_parameters[10], "%d", unit.base_res);
-        sprintf(query_parameters[11], "%d", unit.base_acc);
+        sprintf(query_parameters[4], "%u", unit.base_hp);
+        sprintf(query_parameters[5], "%u", unit.base_atk);
+        sprintf(query_parameters[6], "%u", unit.base_def);
+        sprintf(query_parameters[7], "%u", unit.base_spd);
+        sprintf(query_parameters[8], "%u", unit.base_crr);
+        sprintf(query_parameters[9], "%u", unit.base_crd);
+        sprintf(query_parameters[10], "%u", unit.base_res);
+        sprintf(query_parameters[11], "%u", unit.base_acc);
         // Insert current stats as base stats.
         // Insert current stats as base stats.
         // They will be updated later.
         // They will be updated later.
-        sprintf(query_parameters[12], "%d", unit.base_hp);
-        sprintf(query_parameters[13], "%d", unit.base_atk);
-        sprintf(query_parameters[14], "%d", unit.base_def);
-        sprintf(query_parameters[15], "%d", unit.base_spd);
-        sprintf(query_parameters[16], "%d", unit.base_crr);
-        sprintf(query_parameters[17], "%d", unit.base_crd);
-        sprintf(query_parameters[18], "%d", unit.base_res);
-        sprintf(query_parameters[19], "%d", unit.base_acc);
+        sprintf(query_parameters[12], "%u", unit.base_hp);
+        sprintf(query_parameters[13], "%u", unit.base_atk);
+        sprintf(query_parameters[14], "%u", unit.base_def);
+        sprintf(query_parameters[15], "%u", unit.base_spd);
+        sprintf(query_parameters[16], "%u", unit.base_crr);
+        sprintf(query_parameters[17], "%u", unit.base_crd);
+        sprintf(query_parameters[18], "%u", unit.base_res);
+        sprintf(query_parameters[19], "%u", unit.base_acc);
         // TODO: Get storage status. But how??
         // TODO: Get storage status. But how??
         sprintf(query_parameters[20], "%d", 0);
         sprintf(query_parameters[20], "%d", 0);
-        sprintf(query_parameters[21], "%d", unit.monster);
+        sprintf(query_parameters[21], "%u", unit.monster);
         if (
         if (
           SUCCESS !=
           SUCCESS !=
           db_execute(
           db_execute(

+ 1 - 1
src/RuneOptimizer/update/update_rune_totals.c → src/cli/runeoptimizer/update/update_rune_totals.c

@@ -27,7 +27,7 @@
 #include <sqlite3.h>
 #include <sqlite3.h>
 #include "../error/error.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "../db/db.h"
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "update.h"
 #include "update.h"
 
 
 extern void update_rune_totals(struct DB_Rune *rune){
 extern void update_rune_totals(struct DB_Rune *rune){

+ 1 - 1
src/test/RuneOptimizerE2ETest.c → src/cli/test/runeoptimizer_e2e_test.c

@@ -1,4 +1,4 @@
-#include "RuneOptimizerTest.c"
+#include "runeoptimizer_test.c"
 
 
 /**
 /**
  * Placeholder dummy test.
  * Placeholder dummy test.

+ 1 - 1
src/test/RuneOptimizerIntegrationTest.c → src/cli/test/runeoptimizer_integration_test.c

@@ -1,4 +1,4 @@
-#include "RuneOptimizerTest.c"
+#include "runeoptimizer_test.c"
 
 
 /**
 /**
  * Placeholder dummy test.
  * Placeholder dummy test.

+ 1 - 1
src/test/RuneOptimizerTest.c → src/cli/test/runeoptimizer_test.c

@@ -4,7 +4,7 @@
 #include "unity/unity.c"
 #include "unity/unity.c"
 
 
 #define main app_main
 #define main app_main
-#include "../RuneOptimizer/RuneOptimizer.c"
+#include "../runeoptimizer/runeoptimizer.c"
 #undef main
 #undef main
 
 
 /**
 /**

+ 1 - 1
src/test/RuneOptimizerUnitTest.c → src/cli/test/runeoptimizer_unit_test.c

@@ -1,4 +1,4 @@
-#include "RuneOptimizerTest.c"
+#include "runeoptimizer_test.c"
 
 
 /**
 /**
  * Test the main function for called without any options.
  * Test the main function for called without any options.

+ 0 - 0
src/test/unity/unity.c → src/cli/test/unity/unity.c


+ 0 - 0
src/test/unity/unity.h → src/cli/test/unity/unity.h


+ 0 - 0
src/test/unity/unity_internals.h → src/cli/test/unity/unity_internals.h


+ 0 - 0
RuneOptimizerGUI/__init__.py → src/gui/runeoptimizer_gui/__init__.py


+ 0 - 0
RuneOptimizerGUI/entity/__init__.py → src/gui/runeoptimizer_gui/data/__init__.py


+ 20 - 0
src/gui/runeoptimizer_gui/data/data.py

@@ -0,0 +1,20 @@
+"""
+This file is part of RuneOptimizer.
+
+RuneOptimizer 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.
+
+RuneOptimizer 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
+RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
+
+"""
+
+UNITS = {}
+TEAMS = {}

+ 0 - 0
RuneOptimizerGUI/gui/__init__.py → src/gui/runeoptimizer_gui/database/__init__.py


+ 20 - 0
src/gui/runeoptimizer_gui/database/database.py

@@ -0,0 +1,20 @@
+"""
+This file is part of RuneOptimizer.
+
+RuneOptimizer 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.
+
+RuneOptimizer 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
+RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
+
+"""
+
+# Global database connection
+DB_CONNECTION = None

+ 59 - 56
RuneOptimizerGUI/entity/Rune.py → src/gui/runeoptimizer_gui/entity/Rune.py

@@ -16,6 +16,10 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 
 """
 """
 
 
+import database.database as database
+import values.values as values
+from entity.RuneStat import RuneStat
+
 class Rune():
 class Rune():
     """
     """
     A rune.
     A rune.
@@ -61,7 +65,7 @@ class Rune():
         Gets a stat given by the rune.
         Gets a stat given by the rune.
 
 
     """
     """
-    
+
     _id = None
     _id = None
     _unit = ""
     _unit = ""
     _type = 0
     _type = 0
@@ -83,10 +87,9 @@ class Rune():
     def __init__(self, id=None):
     def __init__(self, id=None):
         if id != None:
         if id != None:
             self._load(id)
             self._load(id)
-            
+
     def _load(self, id):
     def _load(self, id):
-        global conn
-        cursor = conn.execute("""
+        cursor = database.CONNECTION.execute("""
           SELECT
           SELECT
             id,                    --  0
             id,                    --  0
             unit,
             unit,
@@ -145,50 +148,50 @@ class Rune():
             else:
             else:
                 self._unit = None
                 self._unit = None
             self._type = int(row[2])
             self._type = int(row[2])
-            self._type_name = SET_NAMES[self._type]
+            self._type_name = values.SET_NAMES[self._type]
             self._slot = int(row[3])
             self._slot = int(row[3])
             self._stars = int(row[4])
             self._stars = int(row[4])
             self._level = int(row[5])
             self._level = int(row[5])
             self._quality = int(row[6])
             self._quality = int(row[6])
-            self._quality_name = QUALITY_NAMES[self._quality]
+            self._quality_name = values.QUALITY_NAMES[self._quality]
             self._efficiency = float(row[7])
             self._efficiency = float(row[7])
             self._max_efficiency = float(row[8])
             self._max_efficiency = float(row[8])
-            self._stats_current[RUNE_STATS["HP_P"]] = int(row[9])
-            self._stats_current[RUNE_STATS["ATK_P"]] = int(row[10])
-            self._stats_current[RUNE_STATS["DEF_P"]] = int(row[11])
-            self._stats_current[RUNE_STATS["HP"]] = int(row[12])
-            self._stats_current[RUNE_STATS["ATK"]] = int(row[13])
-            self._stats_current[RUNE_STATS["DEF"]] = int(row[14])
-            self._stats_current[RUNE_STATS["SPD"]] = int(row[15])
-            self._stats_current[RUNE_STATS["CRR"]] = int(row[16])
-            self._stats_current[RUNE_STATS["CRD"]] = int(row[17])
-            self._stats_current[RUNE_STATS["RES"]] = int(row[18])
-            self._stats_current[RUNE_STATS["ACC"]] = int(row[19])
-            self._stats_lv12[RUNE_STATS["HP_P"]] = int(row[21])
-            self._stats_lv12[RUNE_STATS["ATK_P"]] = int(row[21])
-            self._stats_lv12[RUNE_STATS["DEF_P"]] = int(row[22])
-            self._stats_lv12[RUNE_STATS["HP"]] = int(row[23])
-            self._stats_lv12[RUNE_STATS["ATK"]] = int(row[22])
-            self._stats_lv12[RUNE_STATS["DEF"]] = int(row[25])
-            self._stats_lv12[RUNE_STATS["SPD"]] = int(row[26])
-            self._stats_lv12[RUNE_STATS["CRR"]] = int(row[27])
-            self._stats_lv12[RUNE_STATS["CRD"]] = int(row[28])
-            self._stats_lv12[RUNE_STATS["RES"]] = int(row[29])
-            self._stats_lv12[RUNE_STATS["ACC"]] = int(row[30])
-            self._stats_lv15[RUNE_STATS["HP_P"]] = int(row[31])
-            self._stats_lv15[RUNE_STATS["ATK_P"]] = int(row[32])
-            self._stats_lv15[RUNE_STATS["DEF_P"]] = int(row[33])
-            self._stats_lv15[RUNE_STATS["HP"]] = int(row[34])
-            self._stats_lv15[RUNE_STATS["ATK"]] = int(row[35])
-            self._stats_lv15[RUNE_STATS["DEF"]] = int(row[36])
-            self._stats_lv15[RUNE_STATS["SPD"]] = int(row[37])
-            self._stats_lv15[RUNE_STATS["CRR"]] = int(row[38])
-            self._stats_lv15[RUNE_STATS["CRD"]] = int(row[39])
-            self._stats_lv15[RUNE_STATS["RES"]] = int(row[40])
-            self._stats_lv15[RUNE_STATS["ACC"]] = int(row[41])
+            self._stats_current[values.RUNE_STATS["HP_P"]] = int(row[9])
+            self._stats_current[values.RUNE_STATS["ATK_P"]] = int(row[10])
+            self._stats_current[values.RUNE_STATS["DEF_P"]] = int(row[11])
+            self._stats_current[values.RUNE_STATS["HP"]] = int(row[12])
+            self._stats_current[values.RUNE_STATS["ATK"]] = int(row[13])
+            self._stats_current[values.RUNE_STATS["DEF"]] = int(row[14])
+            self._stats_current[values.RUNE_STATS["SPD"]] = int(row[15])
+            self._stats_current[values.RUNE_STATS["CRR"]] = int(row[16])
+            self._stats_current[values.RUNE_STATS["CRD"]] = int(row[17])
+            self._stats_current[values.RUNE_STATS["RES"]] = int(row[18])
+            self._stats_current[values.RUNE_STATS["ACC"]] = int(row[19])
+            self._stats_lv12[values.RUNE_STATS["HP_P"]] = int(row[21])
+            self._stats_lv12[values.RUNE_STATS["ATK_P"]] = int(row[21])
+            self._stats_lv12[values.RUNE_STATS["DEF_P"]] = int(row[22])
+            self._stats_lv12[values.RUNE_STATS["HP"]] = int(row[23])
+            self._stats_lv12[values.RUNE_STATS["ATK"]] = int(row[22])
+            self._stats_lv12[values.RUNE_STATS["DEF"]] = int(row[25])
+            self._stats_lv12[values.RUNE_STATS["SPD"]] = int(row[26])
+            self._stats_lv12[values.RUNE_STATS["CRR"]] = int(row[27])
+            self._stats_lv12[values.RUNE_STATS["CRD"]] = int(row[28])
+            self._stats_lv12[values.RUNE_STATS["RES"]] = int(row[29])
+            self._stats_lv12[values.RUNE_STATS["ACC"]] = int(row[30])
+            self._stats_lv15[values.RUNE_STATS["HP_P"]] = int(row[31])
+            self._stats_lv15[values.RUNE_STATS["ATK_P"]] = int(row[32])
+            self._stats_lv15[values.RUNE_STATS["DEF_P"]] = int(row[33])
+            self._stats_lv15[values.RUNE_STATS["HP"]] = int(row[34])
+            self._stats_lv15[values.RUNE_STATS["ATK"]] = int(row[35])
+            self._stats_lv15[values.RUNE_STATS["DEF"]] = int(row[36])
+            self._stats_lv15[values.RUNE_STATS["SPD"]] = int(row[37])
+            self._stats_lv15[values.RUNE_STATS["CRR"]] = int(row[38])
+            self._stats_lv15[values.RUNE_STATS["CRD"]] = int(row[39])
+            self._stats_lv15[values.RUNE_STATS["RES"]] = int(row[40])
+            self._stats_lv15[values.RUNE_STATS["ACC"]] = int(row[41])
 
 
         # Get rune stats
         # Get rune stats
-        cursor = conn.execute(
+        cursor = database.CONNECTION.execute(
           """
           """
             SELECT slot, stat, value, grind, enchant
             SELECT slot, stat, value, grind, enchant
             FROM rune_stats WHERE rune = ?
             FROM rune_stats WHERE rune = ?
@@ -213,27 +216,27 @@ class Rune():
     @property
     @property
     def id(self):
     def id(self):
         return self._id
         return self._id
-    
+
     @property
     @property
     def unit(self):
     def unit(self):
         return self._unit
         return self._unit
-    
+
     @property
     @property
     def type(self):
     def type(self):
         return self._type
         return self._type
-    
+
     @property
     @property
     def type_name(self):
     def type_name(self):
         return self._type_name
         return self._type_name
-    
+
     @property
     @property
     def slot(self):
     def slot(self):
         return self._slot
         return self._slot
-    
+
     @property
     @property
     def stars(self):
     def stars(self):
         return self._stars
         return self._stars
-    
+
     @property
     @property
     def level(self):
     def level(self):
         return self._level
         return self._level
@@ -241,47 +244,47 @@ class Rune():
     @property
     @property
     def quality(self):
     def quality(self):
         return self._quality
         return self._quality
-    
+
     @property
     @property
     def quality_name(self):
     def quality_name(self):
         return self._quality_name
         return self._quality_name
-    
+
     @property
     @property
     def efficiency(self):
     def efficiency(self):
         return self._efficiency
         return self._efficiency
-    
+
     @property
     @property
     def max_efficiency(self):
     def max_efficiency(self):
         return self._max_efficiency
         return self._max_efficiency
-    
+
     @property
     @property
     def main_stat(self):
     def main_stat(self):
         return self._main_stat
         return self._main_stat
-    
+
     @property
     @property
     def innate_stat(self):
     def innate_stat(self):
         return self._innate_stat
         return self._innate_stat
-    
+
     @property
     @property
     def sub_stats(self):
     def sub_stats(self):
         return self._sub_stats
         return self._sub_stats
-    
+
     @property
     @property
     def stats(self):
     def stats(self):
         if self._innate_stat != None:
         if self._innate_stat != None:
             return [self._main_stat] + [self.innate_stat] + self._sub_stats
             return [self._main_stat] + [self.innate_stat] + self._sub_stats
         else:
         else:
             return [self._main_stat] + self._sub_stats
             return [self._main_stat] + self._sub_stats
-    
+
     def get_stat(self, stat, level = None):
     def get_stat(self, stat, level = None):
         """
         """
         Gets a stat given by the rune.
         Gets a stat given by the rune.
-        
+
         Level can be specified as 12 or 15 to get the values the rune will have
         Level can be specified as 12 or 15 to get the values the rune will have
         at that level. If 12 is specified and the rune current level is higher,
         at that level. If 12 is specified and the rune current level is higher,
         it's current value is returned.
         it's current value is returned.
         """
         """
-        if stat in RUNE_STATS.values() == False:
+        if stat in values.RUNE_STATS.values() == False:
             return 0
             return 0
         if int(level) == 12:
         if int(level) == 12:
             return self._stats_lv12[stat]
             return self._stats_lv12[stat]

+ 16 - 14
RuneOptimizerGUI/entity/RuneStat.py → src/gui/runeoptimizer_gui/entity/RuneStat.py

@@ -16,10 +16,12 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 
 """
 """
 
 
+import values.values as values
+
 class RuneStat():
 class RuneStat():
     """
     """
     A stat of a rune.
     A stat of a rune.
-    
+
     Any stat of a rune. Values are validated on setting and capped
     Any stat of a rune. Values are validated on setting and capped
     automatically.
     automatically.
 
 
@@ -51,38 +53,38 @@ class RuneStat():
 
 
     def __init__(self):
     def __init__(self):
         pass
         pass
-    
+
     @property
     @property
     def slot(self):
     def slot(self):
         return self._slot
         return self._slot
-    
+
     @slot.setter
     @slot.setter
     def slot(self, value):
     def slot(self, value):
         value = int(value)
         value = int(value)
         if value < -1 or value > 4:
         if value < -1 or value > 4:
             value = -1;
             value = -1;
         self._slot = value
         self._slot = value
-        
+
     @property
     @property
     def stat(self):
     def stat(self):
         return self._stat
         return self._stat
-    
+
     @stat.setter
     @stat.setter
     def stat(self, value):
     def stat(self, value):
         value = int(value)
         value = int(value)
         if value < 0:
         if value < 0:
             value = 0;
             value = 0;
         self._stat = value
         self._stat = value
-        self._stat_name = STAT_NAMES[value]
-    
+        self._stat_name = values.STAT_NAMES[value]
+
     @property
     @property
     def name(self):
     def name(self):
         return self._stat_name
         return self._stat_name
-    
+
     @property
     @property
     def value(self):
     def value(self):
         return self._value
         return self._value
-    
+
     @value.setter
     @value.setter
     def value(self, value):
     def value(self, value):
         value = int(value)
         value = int(value)
@@ -90,25 +92,25 @@ class RuneStat():
             value = 1;
             value = 1;
         self._value = value
         self._value = value
         self._total = self._value + self._grind
         self._total = self._value + self._grind
-        
+
     @property
     @property
     def grind(self):
     def grind(self):
         return self._grind
         return self._grind
-    
+
     @grind.setter
     @grind.setter
     def grind(self, value):
     def grind(self, value):
         value = int(value)
         value = int(value)
         self._grind = value
         self._grind = value
         self._total = self._value + self._grind
         self._total = self._value + self._grind
-        
+
     @property
     @property
     def total(self):
     def total(self):
         return self._total
         return self._total
-    
+
     @property
     @property
     def is_enchanted(self):
     def is_enchanted(self):
         return self._is_enchanted
         return self._is_enchanted
-    
+
     @is_enchanted.setter
     @is_enchanted.setter
     def is_enchanted(self, value):
     def is_enchanted(self, value):
         if (value == True or int(value) == 1):
         if (value == True or int(value) == 1):

+ 27 - 27
RuneOptimizerGUI/entity/StatSet.py → src/gui/runeoptimizer_gui/entity/StatSet.py

@@ -20,12 +20,12 @@ import math
 class StatSet():
 class StatSet():
     """
     """
     A set of stats.
     A set of stats.
-    
+
     It can be used as a set of stats a unit has in any given moment. Compound
     It can be used as a set of stats a unit has in any given moment. Compound
     stats EHP and DMG can't be set manually and are calculated as the stats
     stats EHP and DMG can't be set manually and are calculated as the stats
     they depend on are set. Values are validated on setting and capped
     they depend on are set. Values are validated on setting and capped
     automatically.
     automatically.
-    
+
     Note that the value caps don't take into acount material monsters, which
     Note that the value caps don't take into acount material monsters, which
     have uniquely low stats. But if you are optimizing your Angelmon runes, I
     have uniquely low stats. But if you are optimizing your Angelmon runes, I
     can't help you.
     can't help you.
@@ -64,14 +64,14 @@ class StatSet():
     _acc = 0
     _acc = 0
     _ehp = 0
     _ehp = 0
     _dmg = 0
     _dmg = 0
-    
+
     def __init__(self):
     def __init__(self):
         pass
         pass
-    
+
     @property
     @property
     def hp(self):
     def hp(self):
         return self._hp
         return self._hp
-    
+
     @hp.setter
     @hp.setter
     def hp(self, value):
     def hp(self, value):
         value = int(value)
         value = int(value)
@@ -79,11 +79,11 @@ class StatSet():
             value = 1;
             value = 1;
         self._hp = value
         self._hp = value
         self._calculate_ehp()
         self._calculate_ehp()
-        
+
     @property
     @property
     def atk(self):
     def atk(self):
         return self._atk
         return self._atk
-    
+
     @atk.setter
     @atk.setter
     def atk(self, value):
     def atk(self, value):
         value = int(value)
         value = int(value)
@@ -91,11 +91,11 @@ class StatSet():
             value = 1;
             value = 1;
         self._atk = value
         self._atk = value
         self._calculate_dmg()
         self._calculate_dmg()
-        
+
     @property
     @property
     def dfc(self):
     def dfc(self):
         return self._dfc
         return self._dfc
-    
+
     @dfc.setter
     @dfc.setter
     def dfc(self, value):
     def dfc(self, value):
         value = int(value)
         value = int(value)
@@ -103,22 +103,22 @@ class StatSet():
             value = 1;
             value = 1;
         self._dfc = value
         self._dfc = value
         self._calculate_ehp()
         self._calculate_ehp()
-        
+
     @property
     @property
     def spd(self):
     def spd(self):
         return self._spd
         return self._spd
-    
+
     @spd.setter
     @spd.setter
     def spd(self, value):
     def spd(self, value):
         value = int(value)
         value = int(value)
         if value < 1:
         if value < 1:
             value = 1;
             value = 1;
         self._spd = value
         self._spd = value
-    
+
     @property
     @property
     def crr(self):
     def crr(self):
         return self._crr
         return self._crr
-    
+
     @crr.setter
     @crr.setter
     def crr(self, value):
     def crr(self, value):
         value = int(value)
         value = int(value)
@@ -128,11 +128,11 @@ class StatSet():
             value = 100
             value = 100
         self._crr = value
         self._crr = value
         self._calculate_dmg()
         self._calculate_dmg()
-    
+
     @property
     @property
     def crd(self):
     def crd(self):
         return self._crd
         return self._crd
-    
+
     @crd.setter
     @crd.setter
     def crd(self, value):
     def crd(self, value):
         value = int(value)
         value = int(value)
@@ -140,11 +140,11 @@ class StatSet():
             value = 50;
             value = 50;
         self._crd = value
         self._crd = value
         self._calculate_dmg()
         self._calculate_dmg()
-        
+
     @property
     @property
     def res(self):
     def res(self):
         return self._res
         return self._res
-    
+
     @res.setter
     @res.setter
     def res(self, value):
     def res(self, value):
         value = int(value)
         value = int(value)
@@ -153,11 +153,11 @@ class StatSet():
         if value > 100:
         if value > 100:
             value = 100
             value = 100
         self._res = value
         self._res = value
-    
+
     @property
     @property
     def acc(self):
     def acc(self):
         return self._acc
         return self._acc
-    
+
     @acc.setter
     @acc.setter
     def acc(self, value):
     def acc(self, value):
         value = int(value)
         value = int(value)
@@ -166,28 +166,28 @@ class StatSet():
         if value > 85:
         if value > 85:
             value = 85
             value = 85
         self._acc = value
         self._acc = value
-        
+
     @property
     @property
     def ehp(self):
     def ehp(self):
         return self._ehp
         return self._ehp
-    
+
     @property
     @property
     def dmg(self):
     def dmg(self):
         return self._dmg
         return self._dmg
-    
+
     def _calculate_ehp(self):
     def _calculate_ehp(self):
         """
         """
         Recalculates _ehp.
         Recalculates _ehp.
-        
+
         Uses _dfc and _hp, and it is called from automatically from their
         Uses _dfc and _hp, and it is called from automatically from their
         setters.
         setters.
         """
         """
         self._ehp = math.ceil((((self._dfc * 3.5) + 1140) * self._hp) / 1000)
         self._ehp = math.ceil((((self._dfc * 3.5) + 1140) * self._hp) / 1000)
-    
+
     def _calculate_dmg(self):
     def _calculate_dmg(self):
         """
         """
         Recalculates _dmg.
         Recalculates _dmg.
-        
+
         Uses _atk, _crr and _crd, and it is called from automatically from
         Uses _atk, _crr and _crd, and it is called from automatically from
         their setters.
         their setters.
         """
         """
@@ -195,7 +195,7 @@ class StatSet():
           (self._atk * (100 - self._crr) / 100) +
           (self._atk * (100 - self._crr) / 100) +
           (self._atk * (      self._crr  / 100) * (self._crd + 100) / 100)
           (self._atk * (      self._crr  / 100) * (self._crd + 100) / 100)
         )
         )
-    
+
     def values(self):
     def values(self):
         """
         """
         Gets all stats as an array.
         Gets all stats as an array.
@@ -206,4 +206,4 @@ class StatSet():
           self._hp,  self._atk, self._dfc, self._spd, self._crr,
           self._hp,  self._atk, self._dfc, self._spd, self._crr,
           self._crd, self._res, self._acc, self._ehp, self._dmg
           self._crd, self._res, self._acc, self._ehp, self._dmg
         ]
         ]
-    
+

+ 15 - 13
RuneOptimizerGUI/entity/Team.py → src/gui/runeoptimizer_gui/entity/Team.py

@@ -16,10 +16,13 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 
 """
 """
 
 
+import database.database as database
+from entity.Unit import Unit
+
 class Team():
 class Team():
     """
     """
     A team of units.
     A team of units.
-    
+
     Very similar to the Teams class, but this one loads units, so it's not
     Very similar to the Teams class, but this one loads units, so it's not
     safe to have as property of Unit.
     safe to have as property of Unit.
 
 
@@ -34,19 +37,18 @@ class Team():
     units : Unit[]
     units : Unit[]
 
 
     """
     """
-    
+
     _id = None
     _id = None
     _name = ""
     _name = ""
     _priority = 1
     _priority = 1
     _units = []
     _units = []
-    
+
     def __init__(self, id=None):
     def __init__(self, id=None):
         if id != None:
         if id != None:
             self._load(id)
             self._load(id)
-            
+
     def _load(self, id):
     def _load(self, id):
-        global conn
-        cursor = conn.execute(
+        cursor = database.CONNECTION.execute(
           "SELECT id, name, priority FROM teams WHERE id = ?",
           "SELECT id, name, priority FROM teams WHERE id = ?",
           (id,)
           (id,)
         )
         )
@@ -55,30 +57,30 @@ class Team():
             self._id = str(row[0])
             self._id = str(row[0])
             self._name = str(row[1])
             self._name = str(row[1])
             self._priority = int(row[2])
             self._priority = int(row[2])
-            
+
             # Load units
             # Load units
             self._units = []
             self._units = []
-            cursor = conn.execute(
+            cursor = database.CONNECTION.execute(
               "SELECT unit FROM units_teams WHERE team = ?",
               "SELECT unit FROM units_teams WHERE team = ?",
               (self._id,)
               (self._id,)
             )
             )
             rows = cursor.fetchall()
             rows = cursor.fetchall()
             for row in rows:
             for row in rows:
                 self.units.append(Unit(str(row[0])))
                 self.units.append(Unit(str(row[0])))
-    
+
     @property
     @property
     def id(self):
     def id(self):
         return self._id
         return self._id
-    
+
     @property
     @property
     def name(self):
     def name(self):
         return self._name
         return self._name
-    
+
     @property
     @property
     def priority(self):
     def priority(self):
         return self._priority
         return self._priority
-    
+
     @property
     @property
     def units(self):
     def units(self):
         return self._units
         return self._units
-    
+

+ 23 - 17
RuneOptimizerGUI/entity/Unit.py → src/gui/runeoptimizer_gui/entity/Unit.py

@@ -16,6 +16,12 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 
 """
 """
 
 
+import database.database as database
+import values.values as values
+from entity.StatSet import StatSet
+from entity.Rune import Rune
+from entity.UnitTeam import UnitTeam
+
 class Unit():
 class Unit():
     """
     """
     A unit.
     A unit.
@@ -57,7 +63,7 @@ class Unit():
         Processes data obtained from RuneOptimizer.
         Processes data obtained from RuneOptimizer.
 
 
     """
     """
-    
+
     _id = None
     _id = None
     _name = ""
     _name = ""
     _stars = 1
     _stars = 1
@@ -74,9 +80,9 @@ class Unit():
     def __init__(self, id=None):
     def __init__(self, id=None):
         if id != None:
         if id != None:
             self._load(id)
             self._load(id)
-            
+
     def _load(self, id):
     def _load(self, id):
-        cursor = conn.execute("""
+        cursor = database.CONNECTION.execute("""
           SELECT
           SELECT
             id,          --  0
             id,          --  0
             name,
             name,
@@ -132,20 +138,20 @@ class Unit():
             self._stats.crd = int(row[18])
             self._stats.crd = int(row[18])
             self._stats.res = int(row[19])
             self._stats.res = int(row[19])
             self._stats.acc = int(row[20])
             self._stats.acc = int(row[20])
-            
+
             # Get runes
             # Get runes
             self._runes = [None, None, None, None, None, None]
             self._runes = [None, None, None, None, None, None]
-            cursor = conn.execute(
+            cursor = database.CONNECTION.execute(
               "SELECT id, slot FROM runes WHERE unit = ?", (self._id,)
               "SELECT id, slot FROM runes WHERE unit = ?", (self._id,)
             )
             )
             rows = cursor.fetchall()
             rows = cursor.fetchall()
             for row in rows:
             for row in rows:
                 self._runes[int(row[1]) - 1] = Rune(str(row[0]))
                 self._runes[int(row[1]) - 1] = Rune(str(row[0]))
                 self._has_runes = True
                 self._has_runes = True
-                
+
             # Get teams
             # Get teams
             self._teams = []
             self._teams = []
-            cursor = conn.execute(
+            cursor = database.CONNECTION.execute(
               "SELECT DISTINCT team FROM units_teams WHERE unit = ?",
               "SELECT DISTINCT team FROM units_teams WHERE unit = ?",
               (self._id,)
               (self._id,)
             )
             )
@@ -159,23 +165,23 @@ class Unit():
     @property
     @property
     def id(self):
     def id(self):
         return self._id
         return self._id
-    
+
     @property
     @property
     def name(self):
     def name(self):
         return self._name
         return self._name
-    
+
     @property
     @property
     def stars(self):
     def stars(self):
         return self._stars
         return self._stars
-    
+
     @property
     @property
     def level(self):
     def level(self):
         return self._level
         return self._level
-    
+
     @property
     @property
     def in_storage(self):
     def in_storage(self):
         return self._in_storage
         return self._in_storage
-    
+
     @property
     @property
     def base_stats(self):
     def base_stats(self):
         return self._base_stats
         return self._base_stats
@@ -183,23 +189,23 @@ class Unit():
     @property
     @property
     def stats(self):
     def stats(self):
         return self._stats
         return self._stats
-    
+
     @property
     @property
     def runes(self):
     def runes(self):
         return self._runes
         return self._runes
-    
+
     @property
     @property
     def has_runes(self):
     def has_runes(self):
         return self._has_runes
         return self._has_runes
-    
+
     @property
     @property
     def teams(self):
     def teams(self):
         return self._teams
         return self._teams
-    
+
     @property
     @property
     def in_teams(self):
     def in_teams(self):
         return self._in_teams
         return self._in_teams
-    
+
     @property
     @property
     def priority(self):
     def priority(self):
         return self._priority
         return self._priority

+ 11 - 9
RuneOptimizerGUI/entity/UnitTeam.py → src/gui/runeoptimizer_gui/entity/UnitTeam.py

@@ -16,10 +16,12 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 
 """
 """
 
 
+import database.database as database
+
 class UnitTeam():
 class UnitTeam():
     """
     """
     A team of a unit.
     A team of a unit.
-    
+
     Very similar to the Team class, but this one doesn't load units, so it's
     Very similar to the Team class, but this one doesn't load units, so it's
     safe to have as property of Unit.
     safe to have as property of Unit.
 
 
@@ -33,17 +35,17 @@ class UnitTeam():
         Team priority, from 1 to 50.
         Team priority, from 1 to 50.
 
 
     """
     """
-    
+
     _id = None
     _id = None
     _name = ""
     _name = ""
     _priority = 1
     _priority = 1
-    
+
     def __init__(self, id=None):
     def __init__(self, id=None):
         if id != None:
         if id != None:
             self._load(id)
             self._load(id)
-            
+
     def _load(self, id):
     def _load(self, id):
-        cursor = conn.execute(
+        cursor = database.CONNECTION.execute(
           "SELECT id, name, priority FROM teams WHERE id = ?",
           "SELECT id, name, priority FROM teams WHERE id = ?",
           (id,)
           (id,)
         )
         )
@@ -52,16 +54,16 @@ class UnitTeam():
             self._id = str(row[0])
             self._id = str(row[0])
             self._name = str(row[1])
             self._name = str(row[1])
             self._priority = int(row[2])
             self._priority = int(row[2])
-    
+
     @property
     @property
     def id(self):
     def id(self):
         return self._id
         return self._id
-    
+
     @property
     @property
     def name(self):
     def name(self):
         return self._name
         return self._name
-    
+
     @property
     @property
     def priority(self):
     def priority(self):
         return self._priority
         return self._priority
-    
+

+ 0 - 0
src/gui/runeoptimizer_gui/entity/__init__.py


+ 3 - 1
RuneOptimizerGUI/gui/DialogConfirm.py → src/gui/runeoptimizer_gui/gui/DialogConfirm.py

@@ -16,10 +16,12 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 
 """
 """
 
 
+import wx
+
 class DialogConfirm(wx.Dialog):
 class DialogConfirm(wx.Dialog):
     """
     """
     Dialog for confirmations.
     Dialog for confirmations.
-    
+
     Simple dialog with a Yes and a No button.
     Simple dialog with a Yes and a No button.
     """
     """
 
 

+ 7 - 4
RuneOptimizerGUI/gui/DialogNewTeam.py → src/gui/runeoptimizer_gui/gui/DialogNewTeam.py

@@ -16,10 +16,13 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 
 """
 """
 
 
+import wx
+import database.database as database
+
 class DialogNewTeam(wx.Dialog):
 class DialogNewTeam(wx.Dialog):
     """
     """
     Dialog for team creation.
     Dialog for team creation.
-    
+
     Allows for name and priority input.
     Allows for name and priority input.
     """
     """
 
 
@@ -66,7 +69,7 @@ class DialogNewTeam(wx.Dialog):
           parent=self, id=wx.ID_CANCEL, pos=(166, 100),
           parent=self, id=wx.ID_CANCEL, pos=(166, 100),
           size=(100, 40), style=wx.LC_REPORT, label="Close"
           size=(100, 40), style=wx.LC_REPORT, label="Close"
         )
         )
-        
+
 
 
     def _accept(self, event=None):
     def _accept(self, event=None):
         error = False
         error = False
@@ -87,11 +90,11 @@ class DialogNewTeam(wx.Dialog):
             )
             )
         if error == False:
         if error == False:
             # TODO: Use a command
             # TODO: Use a command
-            cursor = conn.execute("""
+            cursor = database.CONNECTION.execute("""
               INSERT INTO teams (id, name, priority) VALUES (
               INSERT INTO teams (id, name, priority) VALUES (
                 (SELECT max(CAST(id AS INTEGER)) + 1 FROM teams), ?, ?)
                 (SELECT max(CAST(id AS INTEGER)) + 1 FROM teams), ?, ?)
               """, (name, priority))
               """, (name, priority))
-            conn.commit()
+            DB_CONNECTION.commit()
             reload_teams()
             reload_teams()
             self.EndModal(wx.ID_OK)
             self.EndModal(wx.ID_OK)
 
 

+ 6 - 2
RuneOptimizerGUI/gui/DialogUpdateJson.py → src/gui/runeoptimizer_gui/gui/DialogUpdateJson.py

@@ -16,10 +16,14 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 
 """
 """
 
 
+import os
+import pipes
+import wx
+
 class DialogUpdateJson(wx.Dialog):
 class DialogUpdateJson(wx.Dialog):
     """
     """
     The JSON data updater dialog.
     The JSON data updater dialog.
-    
+
     Allows for JSON file selection and control options. Runs a update command.
     Allows for JSON file selection and control options. Runs a update command.
 
 
     Parameters
     Parameters
@@ -110,7 +114,7 @@ class DialogUpdateJson(wx.Dialog):
         self._accept_button.Bind(wx.EVT_BUTTON, self._accept)
         self._accept_button.Bind(wx.EVT_BUTTON, self._accept)
 
 
         anim = wx.adv.Animation(
         anim = wx.adv.Animation(
-          os.path.dirname(os.path.realpath(__file__)) + '/res/icon/progress.gif'
+          os.path.dirname(os.path.realpath(__file__)) + '/../res/icon/progress.gif'
         )
         )
         progress_ctrl = wx.adv.AnimationCtrl(
         progress_ctrl = wx.adv.AnimationCtrl(
           parent=self, id=wx.ID_ANY, anim=anim, pos=(126, 176), size=(48, 48)
           parent=self, id=wx.ID_ANY, anim=anim, pos=(126, 176), size=(48, 48)

+ 9 - 6
RuneOptimizerGUI/gui/PanelInfo.py → src/gui/runeoptimizer_gui/gui/PanelInfo.py

@@ -16,10 +16,13 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 
 """
 """
 
 
+import wx
+import values.values as values
+
 class PanelInfo(wx.Panel):
 class PanelInfo(wx.Panel):
     """
     """
     The panel that shows info about the app.
     The panel that shows info about the app.
-    
+
     Static content, just some labels.
     Static content, just some labels.
 
 
     """
     """
@@ -56,7 +59,7 @@ class PanelInfo(wx.Panel):
         )
         )
 
 
         wx.StaticText(
         wx.StaticText(
-          parent=self, id=wx.ID_ANY, label=app_info["name"],
+          parent=self, id=wx.ID_ANY, label=values.APP_INFO["name"],
           pos=(30, 30), size=(400, 40)
           pos=(30, 30), size=(400, 40)
         ).SetFont(title_font)
         ).SetFont(title_font)
         wx.StaticText(
         wx.StaticText(
@@ -64,7 +67,7 @@ class PanelInfo(wx.Panel):
           pos=(40, 90), size=(150, 30), style=wx.ALIGN_RIGHT
           pos=(40, 90), size=(150, 30), style=wx.ALIGN_RIGHT
         ).SetFont(label_font)
         ).SetFont(label_font)
         wx.StaticText(
         wx.StaticText(
-          parent=self, id=wx.ID_ANY, label=app_info["app_version"],
+          parent=self, id=wx.ID_ANY, label=values.APP_INFO["app_version"],
           pos=(200, 90), size=(400, 30)
           pos=(200, 90), size=(400, 30)
         ).SetFont(content_font)
         ).SetFont(content_font)
         wx.StaticText(
         wx.StaticText(
@@ -72,7 +75,7 @@ class PanelInfo(wx.Panel):
           pos=(40, 120), size=(150, 30), style=wx.ALIGN_RIGHT
           pos=(40, 120), size=(150, 30), style=wx.ALIGN_RIGHT
         ).SetFont(label_font)
         ).SetFont(label_font)
         wx.StaticText(
         wx.StaticText(
-          parent=self, id=wx.ID_ANY, label=app_info["gui_version"],
+          parent=self, id=wx.ID_ANY, label=values.APP_INFO["gui_version"],
           pos=(200, 120), size=(400, 30)
           pos=(200, 120), size=(400, 30)
         ).SetFont(content_font)
         ).SetFont(content_font)
         wx.StaticText(
         wx.StaticText(
@@ -81,11 +84,11 @@ class PanelInfo(wx.Panel):
         ).SetFont(label_font)
         ).SetFont(label_font)
         wx.StaticText(
         wx.StaticText(
           parent=self, id=wx.ID_ANY,
           parent=self, id=wx.ID_ANY,
-          label=app_info["author"] + " <" +app_info["author_mail"] + ">",
+          label=values.APP_INFO["author"] + " <" +values.APP_INFO["author_mail"] + ">",
           pos=(200, 150), size=(400, 30)
           pos=(200, 150), size=(400, 30)
         ).SetFont(content_font)
         ).SetFont(content_font)
         y = 180
         y = 180
-        for name, url in app_info["url"].items():
+        for name, url in values.APP_INFO["url"].items():
             wx.StaticText(
             wx.StaticText(
               parent=self, id=wx.ID_ANY, label=name + ":",
               parent=self, id=wx.ID_ANY, label=name + ":",
               pos=(40, y), size=(150, 30), style=wx.ALIGN_RIGHT
               pos=(40, y), size=(150, 30), style=wx.ALIGN_RIGHT

+ 86 - 21
RuneOptimizerGUI/gui/PanelOptimizer.py → src/gui/runeoptimizer_gui/gui/PanelOptimizer.py

@@ -15,6 +15,16 @@ You should have received a copy of the GNU General Public License along with
 RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 
 """
 """
+import os
+import signal
+import json
+import multiprocessing
+from types import SimpleNamespace
+import wx
+import wx.grid
+import database.database as database
+import data.data as data
+import values.values as values
 
 
 class PanelOptimizer(wx.Panel):
 class PanelOptimizer(wx.Panel):
     """
     """
@@ -33,7 +43,10 @@ class PanelOptimizer(wx.Panel):
     _selected_unit = None
     _selected_unit = None
     _team_ids = []
     _team_ids = []
     _unit_id_selector_index = []
     _unit_id_selector_index = []
+    _max_combinations = -1
     _options_sizer = None
     _options_sizer = None
+    _optimize_start_sizer = None
+    _optimize_stop_sizer = None
     _unit_choice = None
     _unit_choice = None
     _stat_grid = None
     _stat_grid = None
     _name_label = None
     _name_label = None
@@ -85,20 +98,22 @@ class PanelOptimizer(wx.Panel):
           pointSize=10, family=wx.FONTFAMILY_DEFAULT,
           pointSize=10, family=wx.FONTFAMILY_DEFAULT,
           style=wx.FONTSTYLE_NORMAL, weight=wx.FONTWEIGHT_BOLD
           style=wx.FONTSTYLE_NORMAL, weight=wx.FONTWEIGHT_BOLD
         )
         )
-        
+
         # Prepare the help icon
         # Prepare the help icon
         icon_path = \
         icon_path = \
-          os.path.dirname(os.path.realpath(__file__)) + "/res/icon/help.png"
+          os.path.dirname(os.path.realpath(__file__)) + "/../res/icon/help.png"
         if os.name == 'nt':
         if os.name == 'nt':
             icon_path = os.path.dirname(__file__)
             icon_path = os.path.dirname(__file__)
             if icon_path == "":
             if icon_path == "":
                 icon_path += "."
                 icon_path += "."
-            icon_path += "\\res\\icon\\help.png"
+            icon_path += "\\..\\res\\icon\\help.png"
         help_icon = wx.Bitmap(name=icon_path, type=wx.BITMAP_TYPE_PNG)
         help_icon = wx.Bitmap(name=icon_path, type=wx.BITMAP_TYPE_PNG)
 
 
         # Sizer for all optimization options. Will be hidden until a unit is
         # Sizer for all optimization options. Will be hidden until a unit is
         # selected.
         # selected.
         self._options_sizer = wx.BoxSizer(wx.VERTICAL)
         self._options_sizer = wx.BoxSizer(wx.VERTICAL)
+        self._optimize_start_sizer = wx.BoxSizer(wx.VERTICAL)
+        self._optimize_stop_sizer = wx.BoxSizer(wx.VERTICAL)
 
 
         # Unit info box
         # Unit info box
         unit_info_box = wx.StaticBox(
         unit_info_box = wx.StaticBox(
@@ -107,7 +122,7 @@ class PanelOptimizer(wx.Panel):
         )
         )
 
 
         # Get data from all units and populate the unit selector
         # Get data from all units and populate the unit selector
-        cursor = conn.execute("SELECT id, name FROM units ORDER BY name")
+        cursor = database.CONNECTION.execute("SELECT id, name FROM units ORDER BY name")
         unit_names = []
         unit_names = []
         for row in cursor:
         for row in cursor:
             unit_names.append(row[1])
             unit_names.append(row[1])
@@ -605,7 +620,7 @@ class PanelOptimizer(wx.Panel):
         ]
         ]
         # Team list
         # Team list
         teams_formatted = []
         teams_formatted = []
-        for team in teams.values():
+        for team in data.TEAMS.values():
             self._team_ids.append(team.id)
             self._team_ids.append(team.id)
             teams_formatted.append(team.name + " (" + str(team.priority) + ")")
             teams_formatted.append(team.name + " (" + str(team.priority) + ")")
         teams_box = wx.StaticBox(
         teams_box = wx.StaticBox(
@@ -758,22 +773,39 @@ class PanelOptimizer(wx.Panel):
         self._options_sizer.Add(thread_help)
         self._options_sizer.Add(thread_help)
 
 
         # Button to start
         # Button to start
-        btOptimize = wx.Button(
+        self._optimize_start_button = wx.Button(
           parent=self, id=wx.ID_ANY, pos=(750, 550),
           parent=self, id=wx.ID_ANY, pos=(750, 550),
           size=(100, 40), style=wx.LC_REPORT, label="OPTIMIZE"
           size=(100, 40), style=wx.LC_REPORT, label="OPTIMIZE"
         )
         )
-        self._options_sizer.Add(btOptimize)
-        self.Bind(wx.EVT_BUTTON, self._start_optimization, btOptimize)
+        self._optimize_start_sizer.Add(self._optimize_start_button)
+        self._optimize_start_button.Bind(
+          wx.EVT_BUTTON, self._start_optimization
+        )
+
+        # Button to start
+        self._optimize_stop_button = wx.Button(
+          parent=self, id=wx.ID_ANY, pos=(750, 550),
+          size=(100, 40), style=wx.LC_REPORT, label="STOP"
+        )
+        self._optimize_stop_sizer.Add(self._optimize_stop_button)
+        self._optimize_stop_button.Bind(wx.EVT_BUTTON, self._stop_optimization)
 
 
         # Progress bar
         # Progress bar
+        self._progress_label = wx.StaticText(
+          parent=self, label="", id=wx.ID_ANY,
+          pos=(90, 550), size=(300, 25)
+        )
+        self._options_sizer.Add(self._progress_label)
         self._progress_gauge = wx.Gauge(
         self._progress_gauge = wx.Gauge(
-          parent=self, id=wx.ID_ANY, range=20,
+          parent=self, id=wx.ID_ANY, range=100,
           pos=(50, 535), size=(650, 40), style=wx.GA_HORIZONTAL
           pos=(50, 535), size=(650, 40), style=wx.GA_HORIZONTAL
         )
         )
         self._options_sizer.Add(self._progress_gauge)
         self._options_sizer.Add(self._progress_gauge)
 
 
         # By default, hide all optimization options
         # By default, hide all optimization options
         self._options_sizer.ShowItems(False)
         self._options_sizer.ShowItems(False)
+        self._optimize_start_sizer.ShowItems(False)
+        self._optimize_stop_sizer.ShowItems(False)
 
 
 
 
     def _show_help(self, event, text=None):
     def _show_help(self, event, text=None):
@@ -857,7 +889,7 @@ class PanelOptimizer(wx.Panel):
                     self._unit_choice.SetSelection(i)
                     self._unit_choice.SetSelection(i)
                     break
                     break
 
 
-        self._selected_unit = units[unit_id]
+        self._selected_unit = data.UNITS[unit_id]
 
 
         # Set unit info labels
         # Set unit info labels
         self._name_label.SetLabel(self._selected_unit.name)
         self._name_label.SetLabel(self._selected_unit.name)
@@ -1003,10 +1035,32 @@ class PanelOptimizer(wx.Panel):
         self._opt_set_checklist_list[0].SetCheckedItems(())
         self._opt_set_checklist_list[0].SetCheckedItems(())
         self._opt_set_checklist_list[1].SetCheckedItems(())
         self._opt_set_checklist_list[1].SetCheckedItems(())
 
 
-
         self._options_sizer.ShowItems(True)
         self._options_sizer.ShowItems(True)
+        self._optimize_start_sizer.ShowItems(True)
+
+    def _stop_optimization(self, event=None):
+        """
+        Stops the optimization process.
+
+        Resets window element for another optimization.
+
+        Parameters
+        ----------
+        event : wxEvent, optional
+            The event that triggered the call (default is None).
+
+        """
+        if self._process is not None:
+            #wx.Process.Kill(self._pid())
+            os.kill(self._process.GetPid(), signal.SIGSTOP)
+        if self._timer is not None:
+            self._timer.Stop()
+        self._progress_gauge.SetValue(0)
+        self._progress_label.SetLabel("")
+        self._optimize_stop_sizer.ShowItems(False)
+        self._optimize_start_sizer.ShowItems(True)
 
 
-    def _start_optimization(self, event):
+    def _start_optimization(self, event=None):
         """
         """
         Prepares and runs a command optimization.
         Prepares and runs a command optimization.
 
 
@@ -1039,11 +1093,11 @@ class PanelOptimizer(wx.Panel):
               self._set_choice_list[i].GetSelection()
               self._set_choice_list[i].GetSelection()
             ).upper().replace(" ", "");
             ).upper().replace(" ", "");
             for j in range(0, 22):
             for j in range(0, 22):
-                name = SET_NAMES[j].upper()
+                name = values.SET_NAMES[j].upper()
                 if len(name) > 7:
                 if len(name) > 7:
                     name = name[0:7]
                     name = name[0:7]
                 if selected == name:
                 if selected == name:
-                    sets += SET_NAMES[j].lower() + ","
+                    sets += values.SET_NAMES[j].lower() + ","
         if len(sets) > 0:
         if len(sets) > 0:
             sets = sets[:-1]
             sets = sets[:-1]
             # TODO: ELSE ERROR
             # TODO: ELSE ERROR
@@ -1151,21 +1205,23 @@ class PanelOptimizer(wx.Panel):
 
 
         command += (" --gui ")
         command += (" --gui ")
         print("Command: " + command)
         print("Command: " + command)
+        self._progress_label.SetLabel("")
         self._progress_gauge.SetValue(0)
         self._progress_gauge.SetValue(0)
 
 
         # Timer ot periodically check on the process
         # Timer ot periodically check on the process
         self._timer = wx.Timer(self)
         self._timer = wx.Timer(self)
-        self._timer.Start(1000)
+        self._timer.Start(400)
         self.Bind(wx.EVT_TIMER, self._check_process)
         self.Bind(wx.EVT_TIMER, self._check_process)
 
 
         # Create and execute the process
         # Create and execute the process
         self.Bind(wx.EVT_END_PROCESS, self._optimization_complete)
         self.Bind(wx.EVT_END_PROCESS, self._optimization_complete)
-        #print("Command: " + command)
+        self._optimize_start_sizer.ShowItems(False)
+        self._optimize_stop_sizer.ShowItems(True)
         self._process = wx.Process(self)
         self._process = wx.Process(self)
         self._process.Redirect()
         self._process.Redirect()
         wx.Execute(command, wx.EXEC_ASYNC, self._process)
         wx.Execute(command, wx.EXEC_ASYNC, self._process)
 
 
-    def _check_process(self, event):
+    def _check_process(self, event=None):
         """
         """
         Checks the process output and updates progress bar.
         Checks the process output and updates progress bar.
 
 
@@ -1176,6 +1232,9 @@ class PanelOptimizer(wx.Panel):
 
 
         """
         """
         if self._process is not None:
         if self._process is not None:
+            #wx.Process.Kill(self._pid())
+            os.kill(self._process.GetPid(), signal.SIGUSR1)
+            #signal.signal(signal.SIGUSR1,signalUSR1_handler)
             stream = self._process.GetInputStream()
             stream = self._process.GetInputStream()
             if stream.CanRead():
             if stream.CanRead():
                 text = bytes.decode(stream.read())
                 text = bytes.decode(stream.read())
@@ -1185,9 +1244,12 @@ class PanelOptimizer(wx.Panel):
                 if text.rfind("\n") != -1:
                 if text.rfind("\n") != -1:
                     text = text[text.rfind("\n") + 1:]
                     text = text[text.rfind("\n") + 1:]
 
 
-                # If the line is just a number, it's a progress indicator (1-20)
-                if text.isnumeric() and int(text) <= 20:
-                    self._progress_gauge.SetValue(int(text))
+                self._progress_label.SetLabel(text)
+                for character in '%()/':
+                    text = text.replace(character, '')
+                text = text.strip().split()[1:]
+                if text[0].isnumeric() and int(text[0]) <= 100:
+                    self._progress_gauge.SetValue(int(text[0]))
         else:
         else:
             self._timer.Stop()
             self._timer.Stop()
 
 
@@ -1205,7 +1267,10 @@ class PanelOptimizer(wx.Panel):
         """
         """
 
 
         self._timer.Stop()
         self._timer.Stop()
-        self._progress_gauge.SetValue(20)
+        self._progress_gauge.SetValue(100)
+        self._progress_label.SetLabel("")
+        self._optimize_stop_sizer.ShowItems(False)
+        self._optimize_start_sizer.ShowItems(True)
 
 
         stream = self._process.GetInputStream()
         stream = self._process.GetInputStream()
         json_text = ""
         json_text = ""

+ 49 - 29
RuneOptimizerGUI/gui/PanelResults.py → src/gui/runeoptimizer_gui/gui/PanelResults.py

@@ -16,6 +16,15 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 
 """
 """
 
 
+import math
+import json
+import wx
+import wx.grid
+from types import SimpleNamespace
+import database.database as database
+import data.data as data
+from entity.Rune import Rune
+
 class PanelResults(wx.Panel):
 class PanelResults(wx.Panel):
     """
     """
     The panel that shows results.
     The panel that shows results.
@@ -31,6 +40,7 @@ class PanelResults(wx.Panel):
     _results = None
     _results = None
     _data = None
     _data = None
     _page = 0
     _page = 0
+    _level = 0
     _total_pages = 0
     _total_pages = 0
     _lines_per_page = 10
     _lines_per_page = 10
     _selected_result_index = -1
     _selected_result_index = -1
@@ -97,28 +107,28 @@ class PanelResults(wx.Panel):
         # Unit name, or placeholder message
         # Unit name, or placeholder message
         self._unit_name_label = wx.StaticText(
         self._unit_name_label = wx.StaticText(
             parent=self, id=wx.ID_ANY, label="Nothing yet.\nOptimize something!",
             parent=self, id=wx.ID_ANY, label="Nothing yet.\nOptimize something!",
-            pos=(10, 0), size=(400, 50)
+            pos=(10, 0), size=(400, 100)
         )
         )
         self._unit_name_label.SetFont(title_font)
         self._unit_name_label.SetFont(title_font)
 
 
         #Unit details
         #Unit details
         self._stars_label = wx.StaticText(
         self._stars_label = wx.StaticText(
-          parent=self, label="", pos=(20, 30), size=(120, 100)
+          parent=self, label="", pos=(20, 30), size=(180, 100)
         )
         )
         self._stars_label.SetFont(title_font)
         self._stars_label.SetFont(title_font)
         self._table_sizer.Add(self._stars_label)
         self._table_sizer.Add(self._stars_label)
         self._level_label = wx.StaticText(
         self._level_label = wx.StaticText(
-          parent=self, label="", pos=(20, 60), size=(120, 100)
+          parent=self, label="", pos=(20, 60), size=(180, 100)
         )
         )
         self._table_sizer.Add(self._level_label)
         self._table_sizer.Add(self._level_label)
         self._level_label.SetFont(subtitle_font)
         self._level_label.SetFont(subtitle_font)
         self._priority_label = wx.StaticText(
         self._priority_label = wx.StaticText(
-          parent=self, label="", pos=(20, 90), size=(120, 100)
+          parent=self, label="", pos=(20, 90), size=(180, 100)
         )
         )
         self._priority_label.SetFont(subtitle_font)
         self._priority_label.SetFont(subtitle_font)
         self._table_sizer.Add(self._priority_label)
         self._table_sizer.Add(self._priority_label)
         self._id_label = wx.StaticText(
         self._id_label = wx.StaticText(
-          parent=self, label="", pos=(20, 120), size=(120, 100)
+          parent=self, label="", pos=(20, 120), size=(180, 100)
         )
         )
         self._id_label.SetFont(subtitle_font)
         self._id_label.SetFont(subtitle_font)
         self._table_sizer.Add(self._id_label)
         self._table_sizer.Add(self._id_label)
@@ -598,7 +608,7 @@ class PanelResults(wx.Panel):
         """
         """
 
 
         if unit_id != None:
         if unit_id != None:
-            self._unit = units[unit_id]
+            self._unit = data.UNITS[unit_id]
 
 
         self._update_base_and_current_stats()
         self._update_base_and_current_stats()
         # Read results
         # Read results
@@ -608,12 +618,18 @@ class PanelResults(wx.Panel):
         )
         )
 
 
 
 
-
-        self._total_pages = math.ceil(len(self._results.results) / self._lines_per_page)
+        self._level = self._results.rune_level
+        self._total_pages = (
+          math.ceil(len(self._results.results) / self._lines_per_page)
+        )
         self._page = 0
         self._page = 0
         self._print_results()
         self._print_results()
 
 
-    def _page_prev(self, event):
+        # Select the first row and load it
+        self._result_grid.SelectRow(row=0, addToSelected=False)
+        self._result_selected(event=None)
+
+    def _page_prev(self, event=None):
         """
         """
         Goes to the previous page of results.
         Goes to the previous page of results.
 
 
@@ -631,7 +647,7 @@ class PanelResults(wx.Panel):
             self._page -= 1
             self._page -= 1
             self._print_results()
             self._print_results()
 
 
-    def _page_next(self, event):
+    def _page_next(self, event=None):
         """
         """
         Goes to the next page of results.
         Goes to the next page of results.
 
 
@@ -649,7 +665,7 @@ class PanelResults(wx.Panel):
             self._page += 1
             self._page += 1
             self._print_results()
             self._print_results()
 
 
-    def _apply_runes(self, event):
+    def _apply_runes(self, event=None):
         """
         """
         Applies the selected results and saves data to the database.
         Applies the selected results and saves data to the database.
 
 
@@ -660,13 +676,11 @@ class PanelResults(wx.Panel):
 
 
         """
         """
 
 
-        global conn
-
         if (self._selected_result_index < 0):
         if (self._selected_result_index < 0):
             # TODO: Show error
             # TODO: Show error
             return;
             return;
         # First, unassign all runes currently assigned to the unit
         # First, unassign all runes currently assigned to the unit
-        cursor = conn.execute(
+        cursor = database.CONNECTION.execute(
           """
           """
             UPDATE runes SET unit = null
             UPDATE runes SET unit = null
             WHERE unit = ?
             WHERE unit = ?
@@ -677,7 +691,7 @@ class PanelResults(wx.Panel):
         )
         )
 
 
         # Next, mark units as modified
         # Next, mark units as modified
-        cursor = conn.execute(
+        cursor = database.CONNECTION.execute(
           """
           """
             UPDATE units SET modified = 1
             UPDATE units SET modified = 1
             WHERE
             WHERE
@@ -696,7 +710,7 @@ class PanelResults(wx.Panel):
         )
         )
 
 
         # Lastly, assign the runes
         # Lastly, assign the runes
-        cursor = conn.execute(
+        cursor = database.CONNECTION.execute(
           """
           """
             UPDATE runes SET unit = ?
             UPDATE runes SET unit = ?
             WHERE id IN (?, ?, ?, ?, ?, ?)
             WHERE id IN (?, ?, ?, ?, ?, ?)
@@ -711,7 +725,7 @@ class PanelResults(wx.Panel):
             self._results.results[self._selected_result_index].runes[5]
             self._results.results[self._selected_result_index].runes[5]
           )
           )
         )
         )
-        conn.commit()
+        database.CONNECTION.commit()
         # TODO: Recalculate all modified units stats from the database
         # TODO: Recalculate all modified units stats from the database
         print("Applied!")
         print("Applied!")
         self._recalculte_stats_of_modified_units()
         self._recalculte_stats_of_modified_units()
@@ -785,7 +799,7 @@ class PanelResults(wx.Panel):
         # Make the table visible
         # Make the table visible
         self._table_sizer.ShowItems(True)
         self._table_sizer.ShowItems(True)
 
 
-    def _result_selected(self, event):
+    def _result_selected(self, event=None):
         """
         """
         Populates and shows the runes and effective stats with the
         Populates and shows the runes and effective stats with the
         currently seleced result.
         currently seleced result.
@@ -1069,7 +1083,7 @@ class PanelResults(wx.Panel):
             self._location_label_list[i].SetLabel("")
             self._location_label_list[i].SetLabel("")
 
 
         # Display the runes
         # Display the runes
-        cursor = conn.execute(
+        cursor = database.CONNECTION.execute(
           """
           """
             SELECT
             SELECT
               runes.id,
               runes.id,
@@ -1099,10 +1113,17 @@ class PanelResults(wx.Panel):
             # Rows are 18 charactes width
             # Rows are 18 charactes width
 
 
             # First line: Set name, level
             # First line: Set name, level
-            self._set_label_list[rune.slot - 1].SetLabel(
-              rune.type_name[0:9].ljust(12, " ") + \
-              "+" + str(rune.level).ljust(3, " ")
-            )
+            if self._level > rune.level:
+                self._set_label_list[rune.slot - 1].SetLabel(
+                  rune.type_name[0:9].ljust(9, " ")
+                  + "+" + str(rune.level).ljust(2, " ")
+                  + "\u2794" + str(self._level)
+                )
+            else:
+                self._set_label_list[rune.slot - 1].SetLabel(
+                  rune.type_name[0:9].ljust(12, " ") + \
+                  "+" + str(rune.level).ljust(3, " ")
+                )
 
 
             # Second line: ID
             # Second line: ID
             lvl_set_eff = rune.type_name[0:6].ljust(6, " ")
             lvl_set_eff = rune.type_name[0:6].ljust(6, " ")
@@ -1122,7 +1143,7 @@ class PanelResults(wx.Panel):
             # Source
             # Source
             label_source = ""
             label_source = ""
             if rune.unit is not None:
             if rune.unit is not None:
-                label_source = units[rune.unit].name
+                label_source = data.UNITS[rune.unit].name
             else:
             else:
                 label_source = "In storage"
                 label_source = "In storage"
             self._location_label_list[rune.slot - 1].SetLabel(label_source)
             self._location_label_list[rune.slot - 1].SetLabel(label_source)
@@ -1180,8 +1201,7 @@ class PanelResults(wx.Panel):
         It doen't remove the modified flag.
         It doen't remove the modified flag.
         """
         """
 
 
-        global conn
-        unit_cursor = conn.execute("""
+        unit_cursor = database.CONNECTION.execute("""
           SELECT
           SELECT
             base_hp,
             base_hp,
             base_atk,
             base_atk,
@@ -1197,7 +1217,7 @@ class PanelResults(wx.Panel):
         """)
         """)
         i = 0
         i = 0
         for unit_row in unit_cursor:
         for unit_row in unit_cursor:
-            rune_cursor = conn.execute(
+            rune_cursor = database.CONNECTION.execute(
               """
               """
                 SELECT
                 SELECT
                   sum(current_hp_percent) AS hp,
                   sum(current_hp_percent) AS hp,
@@ -1231,7 +1251,7 @@ class PanelResults(wx.Panel):
             crd = unit_row[5] + rune_row[8]
             crd = unit_row[5] + rune_row[8]
             res = unit_row[6] + rune_row[9]
             res = unit_row[6] + rune_row[9]
             acc = unit_row[7] + rune_row[10]
             acc = unit_row[7] + rune_row[10]
-            conn.execute(
+            database.CONNECTION.execute(
               """
               """
                 UPDATE units SET
                 UPDATE units SET
                   current_hp = ?,
                   current_hp = ?,
@@ -1246,4 +1266,4 @@ class PanelResults(wx.Panel):
               """,
               """,
               (hp, atk, dfc, spd, crr, crd, res, acc, unit_row[8],)
               (hp, atk, dfc, spd, crr, crd, res, acc, unit_row[8],)
             )
             )
-        conn.commit()
+        database.CONNECTION.commit()

+ 23 - 17
RuneOptimizerGUI/gui/PanelTeams.py → src/gui/runeoptimizer_gui/gui/PanelTeams.py

@@ -15,6 +15,12 @@ You should have received a copy of the GNU General Public License along with
 RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 
 """
 """
+import wx
+import wx.grid
+import database.database as database
+import data.data as data
+from gui.DialogConfirm import DialogConfirm
+from gui.DialogNewTeam import DialogNewTeam
 
 
 class PanelTeams(wx.Panel):
 class PanelTeams(wx.Panel):
     """
     """
@@ -24,7 +30,7 @@ class PanelTeams(wx.Panel):
     removing units to and from teams.
     removing units to and from teams.
 
 
     """
     """
-    
+
     _selected_team = None
     _selected_team = None
     _title_change_pending = False
     _title_change_pending = False
     _priority_change_pending = False
     _priority_change_pending = False
@@ -57,7 +63,7 @@ class PanelTeams(wx.Panel):
 
 
         # Parent constructor
         # Parent constructor
         wx.Panel.__init__(self, parent=parent, id=id)
         wx.Panel.__init__(self, parent=parent, id=id)
-        
+
         # Prepare some fonts
         # Prepare some fonts
         bold_font = wx.Font(
         bold_font = wx.Font(
           pointSize=10, family=wx.FONTFAMILY_DEFAULT,
           pointSize=10, family=wx.FONTFAMILY_DEFAULT,
@@ -133,7 +139,7 @@ class PanelTeams(wx.Panel):
           label="Name             Prio.   Sto.",
           label="Name             Prio.   Sto.",
           pos=(10, 10), size=(190, 20)
           pos=(10, 10), size=(190, 20)
         ).SetFont(bold_font)
         ).SetFont(bold_font)
-        
+
         self._all_unit_list = wx.ListCtrl(
         self._all_unit_list = wx.ListCtrl(
           parent=all_units_box, id=wx.ID_ANY, pos=(10, 30), size=(190, 165),
           parent=all_units_box, id=wx.ID_ANY, pos=(10, 30), size=(190, 165),
           style=wx.LC_REPORT|wx.LC_NO_HEADER
           style=wx.LC_REPORT|wx.LC_NO_HEADER
@@ -214,7 +220,7 @@ class PanelTeams(wx.Panel):
         """
         """
         self._team_list.DeleteAllItems()
         self._team_list.DeleteAllItems()
         i = 0
         i = 0
-        for team in teams.values():
+        for team in data.TEAMS.values():
             self._team_list.InsertItem(i, team.name)
             self._team_list.InsertItem(i, team.name)
             self._team_list.SetItem(i, 1, str(team.priority))
             self._team_list.SetItem(i, 1, str(team.priority))
             self._team_list.SetItemData(i, int(team.id))
             self._team_list.SetItemData(i, int(team.id))
@@ -239,7 +245,7 @@ class PanelTeams(wx.Panel):
         opt_no_runes = self._filter_storage_check.GetValue()
         opt_no_runes = self._filter_storage_check.GetValue()
         opt_no_teams = self._filter_no_teams_check.GetValue()
         opt_no_teams = self._filter_no_teams_check.GetValue()
         self._all_unit_list.DeleteAllItems()
         self._all_unit_list.DeleteAllItems()
-        for unit in units.values():
+        for unit in data.UNITS.values():
             if opt_storage == False and unit.in_storage == True:
             if opt_storage == False and unit.in_storage == True:
                 continue
                 continue
             if opt_no_runes == False and unit.has_runes == False:
             if opt_no_runes == False and unit.has_runes == False:
@@ -310,13 +316,13 @@ class PanelTeams(wx.Panel):
         with DialogConfirm(parent=self, message=message) as dlg:
         with DialogConfirm(parent=self, message=message) as dlg:
             if dlg.ShowModal() == wx.ID_OK:
             if dlg.ShowModal() == wx.ID_OK:
                 # TODO: Do this with a command
                 # TODO: Do this with a command
-                cursor = conn.execute(
+                cursor = DB_CONNECTION.execute(
                   "DELETE FROM units_teams WHERE team = ?",
                   "DELETE FROM units_teams WHERE team = ?",
                   (self._team_selected.id,))
                   (self._team_selected.id,))
-                cursor = conn.execute(
+                cursor = DB_CONNECTION.execute(
                   "DELETE FROM teams WHERE id = ?", (self._team_selected.id,)
                   "DELETE FROM teams WHERE id = ?", (self._team_selected.id,)
                 )
                 )
-                conn.commit()
+                DB_CONNECTION.commit()
                 reload_teams()
                 reload_teams()
 
 
                 # Clear the selection and hide the details
                 # Clear the selection and hide the details
@@ -341,9 +347,9 @@ class PanelTeams(wx.Panel):
             unitId = self._all_unit_list.GetItemData(selected_index)
             unitId = self._all_unit_list.GetItemData(selected_index)
             # TODO: Do this with a command
             # TODO: Do this with a command
             query = "INSERT INTO units_teams (team, unit) VALUES (?, ?)";
             query = "INSERT INTO units_teams (team, unit) VALUES (?, ?)";
-            cursor = conn.execute(query, (self._team_selected.id, unitId))
+            cursor = DB_CONNECTION.execute(query, (self._team_selected.id, unitId))
             selected_index = self._all_unit_list.GetNextSelected(selected_index)
             selected_index = self._all_unit_list.GetNextSelected(selected_index)
-        conn.commit()
+        DB_CONNECTION.commit()
         reload_teams();
         reload_teams();
         self._populate_team_unit_list(event=None)
         self._populate_team_unit_list(event=None)
         self._populate_unit_list(event=None)
         self._populate_unit_list(event=None)
@@ -366,9 +372,9 @@ class PanelTeams(wx.Panel):
             unitId = self._team_unit_list.GetItemData(selected_index)
             unitId = self._team_unit_list.GetItemData(selected_index)
             # TODO: Do this with a command
             # TODO: Do this with a command
             query = "DELETE FROM units_teams WHERE team = ? AND unit = ?";
             query = "DELETE FROM units_teams WHERE team = ? AND unit = ?";
-            cursor = conn.execute(query, (self._team_selected.id, unitId))
+            cursor = database.CONNECTION.execute(query, (self._team_selected.id, unitId))
             selected_index = self._team_unit_list.GetNextSelected(selected_index)
             selected_index = self._team_unit_list.GetNextSelected(selected_index)
-        conn.commit()
+        database.CONNECTION.commit()
         reload_teams()
         reload_teams()
         self._populate_team_unit_list(event=None)
         self._populate_team_unit_list(event=None)
         self._populate_unit_list(event=None)
         self._populate_unit_list(event=None)
@@ -387,7 +393,7 @@ class PanelTeams(wx.Panel):
         """
         """
         selected_team_id = \
         selected_team_id = \
           str(self._team_list.GetItemData(self._team_list.GetFirstSelected()))
           str(self._team_list.GetItemData(self._team_list.GetFirstSelected()))
-        self._team_selected = teams[selected_team_id]
+        self._team_selected = data.TEAMS[selected_team_id]
         self._populate_team_unit_list(event=None)
         self._populate_team_unit_list(event=None)
 
 
         # Unbind for the automatic change
         # Unbind for the automatic change
@@ -446,8 +452,8 @@ class PanelTeams(wx.Panel):
                 )
                 )
                 # TODO: Do this with a command
                 # TODO: Do this with a command
                 query = "UPDATE teams SET name = ? WHERE id = ?";
                 query = "UPDATE teams SET name = ? WHERE id = ?";
-                cursor = conn.execute(query, (new_title, self._team_selected.id))
-                conn.commit()
+                cursor = DB_CONNECTION.execute(query, (new_title, self._team_selected.id))
+                DB_CONNECTION.commit()
                 reload_teams()
                 reload_teams()
                 self._populate_team_list(event=None)
                 self._populate_team_list(event=None)
             else:
             else:
@@ -502,8 +508,8 @@ class PanelTeams(wx.Panel):
                 )
                 )
                 # TODO Do this with a command, when there is a command to do it.
                 # TODO Do this with a command, when there is a command to do it.
                 query = "UPDATE teams SET priority = ? WHERE id = ?";
                 query = "UPDATE teams SET priority = ? WHERE id = ?";
-                cursor = conn.execute(query, (new_priority, self._team_selected.id))
-                conn.commit()
+                cursor = DB_CONNECTION.execute(query, (new_priority, self._team_selected.id))
+                DB_CONNECTION.commit()
                 reload_teams()
                 reload_teams()
                 self._populate_team_list(event=None)
                 self._populate_team_list(event=None)
                 self.GetParent().panel_units.populate_unit_list(event=None)
                 self.GetParent().panel_units.populate_unit_list(event=None)

+ 11 - 8
RuneOptimizerGUI/gui/PanelUnits.py → src/gui/runeoptimizer_gui/gui/PanelUnits.py

@@ -15,6 +15,9 @@ You should have received a copy of the GNU General Public License along with
 RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 
 """
 """
+import wx
+import wx.grid
+import data.data as data
 
 
 class PanelUnits(wx.Panel):
 class PanelUnits(wx.Panel):
     """
     """
@@ -153,7 +156,7 @@ class PanelUnits(wx.Panel):
         )
         )
         self._details_sizer.Add(self._name_label)
         self._details_sizer.Add(self._name_label)
         self._name_label.SetFont(title_font)
         self._name_label.SetFont(title_font)
-        
+
         #Unit details
         #Unit details
         self._stars_label = wx.StaticText(
         self._stars_label = wx.StaticText(
           parent=self, label="", pos=(250, 30), size=(120, 100)
           parent=self, label="", pos=(250, 30), size=(120, 100)
@@ -175,7 +178,7 @@ class PanelUnits(wx.Panel):
         )
         )
         self._id_label.SetFont(subtitle_font)
         self._id_label.SetFont(subtitle_font)
         self._details_sizer.Add(self._id_label)
         self._details_sizer.Add(self._id_label)
-        
+
         # Button to go to the ptimizer
         # Button to go to the ptimizer
         optimize_button = wx.Button(
         optimize_button = wx.Button(
           parent=self, id=wx.ID_ANY, pos=(700, 520), size=(120, 50),
           parent=self, id=wx.ID_ANY, pos=(700, 520), size=(120, 50),
@@ -318,7 +321,7 @@ class PanelUnits(wx.Panel):
             pos=(5, 15), size=(130, 10)
             pos=(5, 15), size=(130, 10)
           )
           )
         ]
         ]
-        
+
 
 
         self._main_label_list = [
         self._main_label_list = [
           wx.StaticText(
           wx.StaticText(
@@ -373,7 +376,7 @@ class PanelUnits(wx.Panel):
             pos=(5, 50), size=(130, 10)
             pos=(5, 50), size=(130, 10)
           )
           )
         ]
         ]
-        
+
 
 
         self._stat_label_list = [
         self._stat_label_list = [
           [
           [
@@ -552,7 +555,7 @@ class PanelUnits(wx.Panel):
         opt_no_runes = self._filter_storage_check.GetValue()
         opt_no_runes = self._filter_storage_check.GetValue()
         opt_no_teams = self._filter_no_teams_check.GetValue()
         opt_no_teams = self._filter_no_teams_check.GetValue()
         self._unit_list.DeleteAllItems()
         self._unit_list.DeleteAllItems()
-        for unit in units.values():
+        for unit in data.UNITS.values():
             if opt_storage == False and unit.in_storage == True:
             if opt_storage == False and unit.in_storage == True:
                 continue
                 continue
             if opt_no_runes == False and unit.has_runes == False:
             if opt_no_runes == False and unit.has_runes == False:
@@ -601,7 +604,7 @@ class PanelUnits(wx.Panel):
         unit_id = \
         unit_id = \
           str(self._unit_list.GetItemData(self._unit_list.GetFirstSelected()))
           str(self._unit_list.GetItemData(self._unit_list.GetFirstSelected()))
 
 
-        self._selected_unit = units[unit_id]
+        self._selected_unit = data.UNITS[unit_id]
 
 
         # Set label
         # Set label
         self._name_label.SetLabel(self._selected_unit.name)
         self._name_label.SetLabel(self._selected_unit.name)
@@ -615,7 +618,7 @@ class PanelUnits(wx.Panel):
         )
         )
         self._id_label.SetLabel("#" + self._selected_unit.id)
         self._id_label.SetLabel("#" + self._selected_unit.id)
         self._details_sizer.ShowItems(True)
         self._details_sizer.ShowItems(True)
-        
+
         # Populate stats
         # Populate stats
         self._stat_grid.SetCellValue(
         self._stat_grid.SetCellValue(
           row=0, col=0, s=str(self._selected_unit.base_stats.hp)
           row=0, col=0, s=str(self._selected_unit.base_stats.hp)
@@ -702,7 +705,7 @@ class PanelUnits(wx.Panel):
             eff = ("  Eff:" + str(effv) + "%")
             eff = ("  Eff:" + str(effv) + "%")
             lvl_set_eff += eff
             lvl_set_eff += eff
             self._id_label_list[i].SetLabel(("#" + rune.id).rjust(15, " "))
             self._id_label_list[i].SetLabel(("#" + rune.id).rjust(15, " "))
-            
+
             # Efficiency
             # Efficiency
             label_eff_text = \
             label_eff_text = \
               "Eff.: " + ("{:.1f}".format(rune.efficiency)).rjust(4, " ") + \
               "Eff.: " + ("{:.1f}".format(rune.efficiency)).rjust(4, " ") + \

+ 19 - 15
RuneOptimizerGUI/gui/RuneOptimizerFrame.py → src/gui/runeoptimizer_gui/gui/RuneOptimizerFrame.py

@@ -16,6 +16,11 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 
 """
 """
 
 
+import wx
+import database.database as database
+from gui.TabList import Tab_List
+from gui.DialogUpdateJson import DialogUpdateJson
+
 class RuneOptimizerFrame(wx.Frame):
 class RuneOptimizerFrame(wx.Frame):
     """
     """
     The main application window.
     The main application window.
@@ -30,7 +35,6 @@ class RuneOptimizerFrame(wx.Frame):
 
 
         """
         """
 
 
-        global conn
         super(RuneOptimizerFrame, self).__init__(*args, **kw)
         super(RuneOptimizerFrame, self).__init__(*args, **kw)
 
 
         # Create and configure the menue
         # Create and configure the menue
@@ -39,7 +43,7 @@ class RuneOptimizerFrame(wx.Frame):
 
 
         # Create the statsusbar
         # Create the statsusbar
         status = "Status: "
         status = "Status: "
-        cursor = conn.execute("""
+        cursor = database.CONNECTION.execute("""
           SELECT
           SELECT
             player_id,
             player_id,
             player_name,
             player_name,
@@ -55,7 +59,7 @@ class RuneOptimizerFrame(wx.Frame):
             status = \
             status = \
               row[1] + ", Lv" + str(row[2]) + " (ID #" +str(row[0]) + ")."
               row[1] + ", Lv" + str(row[2]) + " (ID #" +str(row[0]) + ")."
             status += " Last updated " + str(row[3])
             status += " Last updated " + str(row[3])
-            if (row[4] == 1):
+            if row[4] == 1:
                 status += ". Modifications applied since."
                 status += ". Modifications applied since."
             else:
             else:
                 status += ". No modifications applied since."
                 status += ". No modifications applied since."
@@ -65,7 +69,7 @@ class RuneOptimizerFrame(wx.Frame):
         # Create tabs
         # Create tabs
         tabs = Tab_List(parent=pnl, id=wx.ID_ANY)
         tabs = Tab_List(parent=pnl, id=wx.ID_ANY)
 
 
-    def _make_menu_bar(self):
+    def _make_menu_bar(self=None):
         """
         """
         Sets up the application menu.
         Sets up the application menu.
         """
         """
@@ -75,22 +79,22 @@ class RuneOptimizerFrame(wx.Frame):
           -1,
           -1,
           "&Update from JSON file\tCtrl-J",
           "&Update from JSON file\tCtrl-J",
           "Updates the database from a profile JSON file."
           "Updates the database from a profile JSON file."
-        );
+        )
         update_swdb = update_menu.Append(
         update_swdb = update_menu.Append(
           -1,
           -1,
           "&Update from SWDB\tCtrl-W",
           "&Update from SWDB\tCtrl-W",
           "Updates the database from data retrieved from a SWDB instance."
           "Updates the database from data retrieved from a SWDB instance."
-        );
+        )
         update_swarfarm = update_menu.Append(
         update_swarfarm = update_menu.Append(
           -1,
           -1,
           "&Update from Sarfarm\tCtrl-F",
           "&Update from Sarfarm\tCtrl-F",
           "Updates the database from data retrieved from Swarfarm."
           "Updates the database from data retrieved from Swarfarm."
-        );
+        )
         update_sqlite = update_menu.Append(
         update_sqlite = update_menu.Append(
           -1,
           -1,
           "&Update from a sqlite database\tCtrl-Q",
           "&Update from a sqlite database\tCtrl-Q",
           "Updates the database from a SWDB sqlite database."
           "Updates the database from a SWDB sqlite database."
-        );
+        )
 
 
         file_menu = wx.Menu()
         file_menu = wx.Menu()
         file_about = file_menu.Append(wx.ID_ABOUT)
         file_about = file_menu.Append(wx.ID_ABOUT)
@@ -111,7 +115,7 @@ class RuneOptimizerFrame(wx.Frame):
         self.Bind(wx.EVT_MENU, self._update_from_swarfarm, update_swarfarm)
         self.Bind(wx.EVT_MENU, self._update_from_swarfarm, update_swarfarm)
         self.Bind(wx.EVT_MENU, self._update_from_sqlite, update_sqlite)
         self.Bind(wx.EVT_MENU, self._update_from_sqlite, update_sqlite)
 
 
-    def _close_app(self, event):
+    def _close_app(self, event=None):
         """
         """
         Closes the app.
         Closes the app.
 
 
@@ -124,7 +128,7 @@ class RuneOptimizerFrame(wx.Frame):
 
 
         self.Close(True)
         self.Close(True)
 
 
-    def _show_about(self, event):
+    def _show_about(self, event=None):
         """
         """
         Display an About dialog.
         Display an About dialog.
 
 
@@ -142,7 +146,7 @@ class RuneOptimizerFrame(wx.Frame):
           style=wx.OK | wx.ICON_INFORMATION
           style=wx.OK | wx.ICON_INFORMATION
         )
         )
 
 
-    def _update_from_json(self, event = None):
+    def _update_from_json(self, event=None):
         """
         """
         Shows a dialog to update data from a JSON file.
         Shows a dialog to update data from a JSON file.
 
 
@@ -156,11 +160,11 @@ class RuneOptimizerFrame(wx.Frame):
         with DialogUpdateJson(self) as dlg:
         with DialogUpdateJson(self) as dlg:
             if dlg.ShowModal() == wx.ID_OK:
             if dlg.ShowModal() == wx.ID_OK:
                 # do something here
                 # do something here
-                if (dlg.update_done == True and dlg.update_error == False):
+                if (dlg.update_done is True and dlg.update_error is False):
                     print("Update succesfull!")
                     print("Update succesfull!")
                     # TODO: Recalculate status bas message
                     # TODO: Recalculate status bas message
 
 
-    def _update_from_swdb(self, event):
+    def _update_from_swdb(self, event=None):
         """
         """
         Updates data from a SWDB instance.
         Updates data from a SWDB instance.
 
 
@@ -175,7 +179,7 @@ class RuneOptimizerFrame(wx.Frame):
 
 
         self._show_unimplemented()
         self._show_unimplemented()
 
 
-    def _update_from_swarfarm(self, event):
+    def _update_from_swarfarm(self, event=None):
         """
         """
         Updates data from Swarfarm.
         Updates data from Swarfarm.
 
 
@@ -190,7 +194,7 @@ class RuneOptimizerFrame(wx.Frame):
 
 
         self._show_unimplemented()
         self._show_unimplemented()
 
 
-    def _update_from_sqlite(self, event):
+    def _update_from_sqlite(self, event=None):
         """
         """
         Updates data from a Sqlite file.
         Updates data from a Sqlite file.
 
 

+ 15 - 7
RuneOptimizerGUI/gui/TabList.py → src/gui/runeoptimizer_gui/gui/TabList.py

@@ -15,6 +15,14 @@ You should have received a copy of the GNU General Public License along with
 RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 
 """
 """
+import os
+import wx
+import wx.grid
+from gui.PanelUnits import PanelUnits
+from gui.PanelTeams import PanelTeams
+from gui.PanelOptimizer import PanelOptimizer
+from gui.PanelResults import PanelResults
+from gui.PanelInfo import PanelInfo
 
 
 class Tab_List(wx.Listbook):
 class Tab_List(wx.Listbook):
     """
     """
@@ -41,23 +49,23 @@ class Tab_List(wx.Listbook):
     _panel_optimizer = None
     _panel_optimizer = None
     _panel_results = None
     _panel_results = None
     _panel_info = None
     _panel_info = None
-    
+
     @property
     @property
     def panel_units(self):
     def panel_units(self):
         return self._panel_units
         return self._panel_units
-    
+
     @property
     @property
     def panel_teams(self):
     def panel_teams(self):
         return self._panel_teams
         return self._panel_teams
-    
+
     @property
     @property
     def panel_optimizer(self):
     def panel_optimizer(self):
         return self._panel_optimizer
         return self._panel_optimizer
-    
+
     @property
     @property
     def panel_results(self):
     def panel_results(self):
         return self._panel_results
         return self._panel_results
-    
+
     @property
     @property
     def panel_info(self):
     def panel_info(self):
         return self._panel_inif
         return self._panel_inif
@@ -86,12 +94,12 @@ class Tab_List(wx.Listbook):
 
 
         # Load the icons
         # Load the icons
         icon_path = \
         icon_path = \
-          os.path.dirname(os.path.realpath(__file__)) + "/res/icon/"
+          os.path.dirname(os.path.realpath(__file__)) + "/../res/icon/"
         if os.name == 'nt':
         if os.name == 'nt':
             icon_path = os.path.dirname(__file__)
             icon_path = os.path.dirname(__file__)
             if icon_path == "":
             if icon_path == "":
                 icon_path += "."
                 icon_path += "."
-            icon_path += "\\res\\icon\\"
+            icon_path += "\\..\\res\\icon\\"
         il = wx.ImageList(50, 50)
         il = wx.ImageList(50, 50)
         for ico in [
         for ico in [
           "units.png", "teams.png", "optimize.png", "results.png", "info.png"
           "units.png", "teams.png", "optimize.png", "results.png", "info.png"

+ 0 - 0
src/gui/runeoptimizer_gui/gui/__init__.py


+ 0 - 0
RuneOptimizerGUI/res/icon/help.png → src/gui/runeoptimizer_gui/res/icon/help.png


+ 0 - 0
RuneOptimizerGUI/res/icon/info.png → src/gui/runeoptimizer_gui/res/icon/info.png


+ 0 - 0
RuneOptimizerGUI/res/icon/optimize.png → src/gui/runeoptimizer_gui/res/icon/optimize.png


+ 0 - 0
RuneOptimizerGUI/res/icon/progress.gif → src/gui/runeoptimizer_gui/res/icon/progress.gif


+ 0 - 0
RuneOptimizerGUI/res/icon/results.png → src/gui/runeoptimizer_gui/res/icon/results.png


+ 0 - 0
RuneOptimizerGUI/res/icon/teams.png → src/gui/runeoptimizer_gui/res/icon/teams.png


+ 0 - 0
RuneOptimizerGUI/res/icon/units.png → src/gui/runeoptimizer_gui/res/icon/units.png


+ 106 - 0
src/gui/runeoptimizer_gui/runeoptimizer_gui.py

@@ -0,0 +1,106 @@
+#!/usr/bin/env python
+
+"""
+This file is part of RuneOptimizer.
+
+RuneOptimizer 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.
+
+RuneOptimizer 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
+RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
+
+"""
+
+import sqlite3
+import subprocess
+import json
+import os
+import signal
+import pipes
+import appdirs
+from types import SimpleNamespace
+from time import sleep
+import wx
+import wx.grid
+import wx.adv
+import data.data as data
+import values.values as values
+import database.database as database
+from entity.Team import Team
+from entity.Unit import Unit
+from gui.RuneOptimizerFrame import RuneOptimizerFrame
+
+# The path to the RuneOptimizer executable. Different in Windows.
+executable_path = \
+  os.path.dirname(os.path.realpath(__file__)) + '/../RuneOptimizer'
+if os.name == 'nt':
+    executable_path = \
+      os.path.dirname(os.path.realpath(__file__)) + '\\..\\RuneOptimizer.exe'
+
+def reload_units():
+    # Get all the units, sorted by priority desc
+    data.UNITS = {}
+    cursor = database.CONNECTION.execute("""
+      SELECT
+        id,
+        (
+          SELECT sum(priority)
+          FROM teams
+          WHERE id IN (SELECT team FROM units_teams WHERE unit = units.id)
+        ) AS prio
+      FROM units
+      ORDER BY prio DESC;
+    """)
+    rows = cursor.fetchall()
+    for row in rows:
+        data.UNITS[str(row[0])] = Unit(str(row[0]))
+
+def reload_teams():
+    # Get all the teams, sorted by priority desc
+    data.TEAMS = {}
+    cursor = database.CONNECTION.execute(
+      "SELECT id FROM teams ORDER BY priority DESC;"
+    )
+    rows = cursor.fetchall()
+    for row in rows:
+        data.TEAMS[str(row[0])] = Team(str(row[0]))
+
+if __name__ == '__main__':
+    """
+    Main function.
+
+    Conects to the database and shows the initial frame.
+    """
+
+    #Get CLI app version
+    # TODO: Change executable name for windows
+    #if os.name == 'nt':
+    values.APP_INFO["app_version"] = subprocess.check_output(
+      ['runeoptimizer', 'version']
+    )
+
+    # TODO: Test for executable to exist
+    # TODO: Test for database to exist
+
+    # Open the database
+    database.CONNECTION = sqlite3.connect(
+      appdirs.user_data_dir("RuneOptimizer") + "/data.sqlite"
+    )
+
+    reload_units()
+    reload_teams()
+
+    # Start the GUI
+    app = wx.App()
+    frm = RuneOptimizerFrame(
+      parent=None, title='Rune Optimizer', pos=(100, 100), size=(1000, 650)
+    )
+    frm.Show()
+    app.MainLoop()

+ 0 - 0
src/gui/runeoptimizer_gui/values/__init__.py


+ 73 - 0
src/gui/runeoptimizer_gui/values/values.py

@@ -0,0 +1,73 @@
+"""
+This file is part of RuneOptimizer.
+
+RuneOptimizer 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.
+
+RuneOptimizer 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
+RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
+
+"""
+
+APP_INFO = {
+    "name": "Rune Optimizer",
+    "app_version": "UNKNOWN",
+    "gui_version": "0.1-RC1",
+    "author": "Iñigo Valentin",
+    "author_mail": "i@inigovalentin.com",
+    "url": {
+        "Home page": "https://inigovalentin.com/projects/RuneOptimizer/",
+        "Source code": "https://github.com/InigoValentin/RuneOptimizer/"
+    }
+}
+
+RUNE_STATS = {
+    "HP": 1, "HP_P": 2, "ATK": 3, "ATK_P": 4, "DEF": 5, "DEF_P": 6,
+    "SPD": 8, "CRR": 9, "CRD": 10, "RES": 11, "ACC": 12
+}
+
+# Unit stat names, indexed with Com2Us values.
+STAT_NAMES = [
+  "NULL", "HP  ", "HP% ", "ATK ", "ATK%", "DEF ",
+  "DEF%", "NULL", "SPD ", "CRR ", "CRD ", "RES ", "ACC "
+]
+
+# Rune set names, indexed with Com2Us values.
+SET_NAMES = [
+  "NULL",    "ENERGY",  "GUARD",    "SWIFT",   "BLADE",    "RAGE",     #  0- 5
+  "FOCUS",   "ENDURE",  "FATAL",    "NULL",    "DESPAIR",  "VAMPIRE",  #  6-11
+  "NULL",    "VIOLENT", "NEMESIS",  "WILL",    "SHIELD",   "REVENGE",  # 12-17
+  "DESTROY", "FIGHT",   "DETERMIN", "ENHANCE", "ACCURACY", "TOLERANCE" # 18-23
+]
+
+# TODO: Work in progress...
+"""
+# Rune set names, indexed with Com2Us values.
+SET_SYMBOLS = [
+  "NULL",    "ENERGY",  "GUARD",    "SWIFT",   "ᚬ",    "ᛟ",     #  0- 5
+  "FOCUS",   "ENDURE",  "FATAL",    "NULL",    "ᛃ",  "VAMPIRE",  #  6-11
+  "NULL",    "ᛒ", "NEMESIS",  "WILL",    "SHIELD",   "ᛝ",  # 12-17
+  "DESTROY", "FIGHT",   "ᛗ", "ENHANCE", "ACCURACY", "TOLERANCE" # 18-23
+]
+
+SET_SYMBOLS_UNICODE = [
+  "NULL",    "ENERGY",  "GUARD",    "SWIFT",   "\u16AC",    "\u16DF", #  0- 5
+  "FOCUS",   "ENDURE",  "FATAL",    "NULL",    "\u16C3",  "VAMPIRE",  #  6-11
+  "NULL",    "\u16D2", "NEMESIS",  "WILL",    "SHIELD",   "\u16DD",  # 12-17
+  "DESTROY", "FIGHT",   "\u16D7", "ENHANCE", "ACCURACY", "TOLERANCE" # 18-23
+]
+"""
+
+QUALITY_NAMES = [
+  "NULL",
+  "NORMAL", "MAGIC", "RARE", "HERO", "LEGEND"
+  "NULL", "NULL", "NULL", "NULL", "NULL", "NULL",
+  "A.NORMAL", "A.MAGIC", "A.RARE", "A.HERO",  "A.LEGEND"
+]