Parcourir la source

Merge branch 'master' into Optimizer

Iñigo Valentin il y a 5 ans
Parent
commit
6a82ed20fa
100 fichiers modifiés avec 8086 ajouts et 1246 suppressions
  1. 1 1
      application/API/v1/API_Controller.php
  2. 4 0
      application/API/v2/API_Controller.php
  3. 156 0
      application/API/v2/bin/update_collection.py
  4. 2 1
      application/API/v2/bin/upload_run_dungeon.py
  5. 3 1
      application/API/v2/help/api.php
  6. 2 1
      application/API/v2/help/index.php
  7. 1 3
      application/API/v2/units.php
  8. 87 0
      application/API/v2/update_collection.php
  9. 3 3
      application/API/v2/update_logbook.php
  10. 0 1
      application/API/v2/upload_run_dimension.php
  11. 0 1
      application/API/v2/upload_run_dungeon.php
  12. 0 1
      application/API/v2/upload_run_rift.php
  13. 0 1
      application/API/v2/upload_run_scenario.php
  14. 0 1
      application/API/v2/upload_run_toa.php
  15. 98 0
      application/API/v3/API_Controller.php
  16. 425 0
      application/API/v3/help/api.php
  17. 488 0
      application/API/v3/help/index.php
  18. 17 0
      application/API/v3/main.php
  19. 77 0
      application/API/v3/profile/DELETE.php
  20. 67 0
      application/API/v3/profile/GET.php
  21. 211 0
      application/API/v3/profile/POST.php
  22. 1659 0
      application/API/v3/profile/PUT.php
  23. 1469 0
      application/API/v3/run/POST.php
  24. 455 0
      application/API/v3/units/GET.php
  25. 1357 2
      application/Constant.php
  26. 7 3
      application/Controller.php
  27. 6 7
      application/action/delete_team.php
  28. 10 10
      application/action/edit_profile.php
  29. 24 14
      application/action/login.php
  30. 1 2
      application/action/logout.php
  31. 17 10
      application/action/new_team.php
  32. 1 4
      application/action/optimize_get_options.php
  33. 14 25
      application/action/optimize_get_rune_list.php
  34. 14 8
      application/action/rate_team.php
  35. 34 14
      application/action/save_run_team.php
  36. 45 51
      application/entity/Artifact.php
  37. 15 13
      application/entity/Building.php
  38. 15 13
      application/entity/Decoration.php
  39. 47 32
      application/entity/Enchantment.php
  40. 43 38
      application/entity/Fusion.php
  41. 29 25
      application/entity/Guild.php
  42. 18 17
      application/entity/Guild_Member.php
  43. 21 17
      application/entity/Inventory.php
  44. 28 11
      application/entity/K_Area.php
  45. 10 9
      application/entity/K_Building.php
  46. 10 9
      application/entity/K_Decoration.php
  47. 11 10
      application/entity/K_Effect.php
  48. 11 10
      application/entity/K_Guild_Skill.php
  49. 20 17
      application/entity/K_Guild_Skill_Group.php
  50. 22 18
      application/entity/K_Inventory.php
  51. 20 17
      application/entity/K_Leader_Skill.php
  52. 11 10
      application/entity/K_Rune_Set.php
  53. 51 44
      application/entity/K_Skill.php
  54. 13 11
      application/entity/K_Skill_Level.php
  55. 11 10
      application/entity/K_Source.php
  56. 67 60
      application/entity/K_Unit.php
  57. 6 6
      application/entity/K_Unit_Transformation.php
  58. 58 46
      application/entity/Player.php
  59. 49 43
      application/entity/Record.php
  60. 267 205
      application/entity/Run.php
  61. 47 46
      application/entity/Rune.php
  62. 36 33
      application/entity/Team.php
  63. 105 81
      application/entity/Unit.php
  64. 1 1
      application/helper/APPLICATION_Helper.php
  65. 24 90
      application/helper/HTML_Helper.php
  66. 0 1
      application/page/Artifacts_Page.php
  67. 17 0
      application/page/Catalog_Page.php
  68. 6 8
      application/page/Enchantments_Page.php
  69. 59 0
      application/page/Error_Page.php
  70. 1 1
      application/page/Fusion_Page.php
  71. 9 3
      application/page/Landing_Page.php
  72. 0 2
      application/page/Monster_Info_Page.php
  73. 0 2
      application/page/Monster_Page.php
  74. 0 2
      application/page/Monsters_Page.php
  75. 1 1
      application/page/Optimizer_Page.php
  76. 49 50
      application/page/Report_Rune_Enchantment_Page.php
  77. 0 1
      application/page/Report_Skillup_Page.php
  78. 0 7
      application/page/Stats_Page.php
  79. 6 6
      application/page/Teams_Page.php
  80. BIN
      application/sw.sqlite
  81. 3 2
      application/view/artifacts.php
  82. 9 3
      application/view/catalog.php
  83. 3 4
      application/view/enchantments.php
  84. 45 0
      application/view/error.php
  85. 2 2
      application/view/fusion.php
  86. 1 1
      application/view/guild.php
  87. 1 1
      application/view/help.php
  88. 3 2
      application/view/home.php
  89. 2 0
      application/view/inc/header.php
  90. 12 1
      application/view/landing.php
  91. 3 3
      application/view/monster.php
  92. 3 2
      application/view/monster_info.php
  93. 3 3
      application/view/monsters.php
  94. 2 2
      application/view/optimizer.php
  95. 3 11
      application/view/optimizer_unit.php
  96. 3 3
      application/view/profile.php
  97. 3 2
      application/view/report.php
  98. 10 9
      application/view/report_rune_enchantment.php
  99. 3 3
      application/view/report_skillup.php
  100. 3 1
      application/view/runes.php

+ 1 - 1
application/API/v1/API_Controller.php

@@ -32,7 +32,7 @@
                 require_once(__DIR__ . "main.php");
             }
             else{
-                $command = strtoupper$pars[1];
+                $command = strtoupper($params[1]);
                 switch ($command){
                     case "log-profile":
                         require_once(__DIR__ . "log-profile/index.php");

+ 4 - 0
application/API/v2/API_Controller.php

@@ -38,6 +38,7 @@
                 switch ($command){
                     case "help":
                         require_once(__DIR__ . "/help/index.php");
+                        break;
                     case "upload_profile":
                         require_once(__DIR__ . "/upload_profile.php");
                         break;
@@ -59,6 +60,9 @@
                     case "update_logbook":
                         require_once(__DIR__ . "/update_logbook.php");
                         break;
+                    case "update_collection":
+                        require_once(__DIR__ . "/update_collection.php");
+                        break;
                     case "units":
                         require_once(__DIR__ . "/units.php");
                         break;

+ 156 - 0
application/API/v2/bin/update_collection.py

@@ -0,0 +1,156 @@
+#!/usr/bin/python3
+
+import sqlite3
+import json
+import sys
+import os
+
+
+"""
+Reads the API KEY, that must be passed as first command line argument.
+
+:returns: Recovered API KEY.
+:raises Exception: Th KEY couldn't be red.
+"""
+def readKey():
+    try:
+        #print(sys.argv[0])
+        key = sys.argv[1]
+        return key
+    except Exception as e:
+        print("Error parsing API KEY: " + str(e))
+        raise
+
+"""
+Reads the JSON data, that must be passed as second command line argument.
+
+:param: index 2 for request data, 3 for response data
+:returns: Recovered data, in JSON format.
+:raises Exception: The data couldn't be red or converted to JSON.
+"""
+def readData(index):
+    try:
+        data = json.loads(sys.argv[index])
+        return data
+    except Exception as e:
+        print("Error parsing data: " + str(e))
+        raise
+
+"""
+Verifies that the API key matches the player data and that it exists in th DB.
+
+:param db: Connection to the database.
+:returns: Connection to the database.
+:param data: Data in json format.
+:param key: API KEY.
+:returns: True if key and player match, False otherwise.
+:raises IntegrityError: The queryes couldn't bre executed.
+"""
+def verifyKey(db, data, key):
+    print('Verifying KEY...')
+    status = False
+    try:
+        uid = data["wizard_id"]
+        cursor = db.cursor()
+        cursor.execute('SELECT count(uid) AS c FROM player WHERE uid = ? AND api_key = ?;', (uid, key))
+        if cursor.fetchone()[0] == 1:
+            status = True
+        cursor.close()
+    except sqlite3.IntegrityError as e:
+        print("Error executing statement: " + str(e))
+        raise
+    return status
+
+"""
+Opens the database file and deletes from the user tables
+
+:param name: The path to the sqlite database.
+:returns: Connection to the database.
+:raises IntegrityError: The queryes couldn't bre executed.
+:raises IOError: The sqlite file couldn't be created.
+"""
+def openDatabase():
+    kdb = os.path.dirname(os.path.realpath(sys.argv[0])) + '/../../../sw.sqlite'
+    udb = os.path.dirname(os.path.realpath(sys.argv[0])) + '/../../../../data/sw.sqlite'
+    print('Configuring database...')
+    try:
+        db = sqlite3.connect(kdb)
+        cursor = db.cursor()
+        cursor.execute('attach "' + udb + '" as data;')
+        cursor.close()
+        return db
+    except sqlite3.IntegrityError as e:
+        print("Error executing statement: " + str(e))
+        raise
+    except IOError as e:
+        print("I/O Error creating database " + name + ": " + str(e))
+        raise
+
+"""
+Inserts a row into the database.
+
+:param db: Connection to the database.
+:param table: Name of the table to insert into.
+:param values: List of values to insert.
+:raises IntegrityError: The insert query was unsuccesfull.
+"""
+def insert(db, table, values):
+    cursor = db.cursor()
+    placeholders = ''
+    for x in range(0, len(values)):
+        placeholders = placeholders + '?, '
+    placeholders = placeholders[:len(placeholders) - 2]
+    query = 'INSERT INTO ' + table + ' VALUES (' + placeholders + ')'
+    try:
+        cursor.execute(query, values)
+    except sqlite3.IntegrityError as e:
+        print('Error Inserting into ' + table + ' with values (' + str(values) + '): ' + str(e))
+        raise
+    cursor.close;
+
+"""
+Parses run data (tables run, run_party, run_drop_rune, run_drop_runecraft,
+run_drop_item, run_drop_unit, run_drop_sd, run_drop_unit_pieces,
+run_drop_shapeshifting).
+
+:param db: Sqlite database connection.
+:param data: JSON data.
+"""
+def parseData(db, data_request, data_response):
+
+    cursor = db.cursor()
+
+    # Page 1: Cairos non-elemental, rift raid, rift dungeon.
+    print("Parsing collection data...")
+    
+    uid = data_request["wizard_id"]
+    collection = data_response["collection"]
+    cursor.execute("""
+            DELETE FROM collection
+            WHERE uid = ?;
+        """,
+        [uid]
+    )
+
+    # Loop Cairos records
+    for u in collection:
+        unit = u["unit_master_id"]
+        open = u["open"]
+        insert(db, "collection", (uid, unit, open))
+    db.commit()
+
+"""
+Begin script
+"""
+data_request = readData(2)
+data_response = readData(3)
+key = readKey()
+db = openDatabase()
+if verifyKey(db, data_request, key) == False:
+    print("Invalid API KEY...")
+    sys.exit(-1)
+else:
+    parseData(db, data_request, data_response)
+sys.exit(0)
+
+

+ 2 - 1
application/API/v2/bin/upload_run_dungeon.py

@@ -150,6 +150,7 @@ def parseRun(db, data_request, data_response):
         crystal = mana = data_response["reward"]["crystal"]
     else:
         crystal = 0
+    guild_points = 0
     # TODO Read helper. Do a test with SWBD-debug
     helper = 0
 
@@ -161,7 +162,7 @@ def parseRun(db, data_request, data_response):
 
     score = None
     rank = None
-    insert(db, "run", (uid, id, dtime, area_type, area, stage, difficulty, win, time, mana, energy, crystal, helper, score, rank))
+    insert(db, "run", (uid, id, dtime, area_type, area, stage, difficulty, win, time, mana, energy, crystal, guild_points, helper, score, rank))
 
     # Parse various types of reward
     crate = data_response["reward"]["crate"]

+ 3 - 1
application/API/v2/help/api.php

@@ -1,5 +1,7 @@
 <?php
-
+    /**
+     * @var mixed[] $API API help structure.
+     */
     $API = [
         "version" => 2,
         "category" => [

+ 2 - 1
application/API/v2/help/index.php

@@ -5,6 +5,7 @@
      * Shows API documentation.
      *
      * @category View
+     * @var mixed[] $API API help structure.
      */
 ?>
 <?php
@@ -320,7 +321,7 @@
                     foreach ($category["command"] as $command){
 ?>
                         <li>
-                            <a href='#command_<?=$command["name"]?>' alt='<?=$command["description"]?>'>
+                            <a href='#command_<?=$command["name"]?>' title='<?=$command["description"]?>'>
                                 <?=$command["name"]?>
                             </a>
                         </li>

+ 1 - 3
application/API/v2/units.php

@@ -7,9 +7,7 @@
      *  - key: User API key. If not, only public user's unit can be seen.
      *
      * @category API
-     * @magic $params URL parameters:
-     *  1: User ID
-     *  2: Unit ID (optional)
+     * @var mixed[] $params URL parameters.
      */
 
     global $db;

+ 87 - 0
application/API/v2/update_collection.php

@@ -0,0 +1,87 @@
+<?php
+    /**
+     * Logbook logger script.
+     *
+     * Exposes an API to update the logbook for player data and records.
+     * Reads post data and calls the update_logbook.py script.
+     * Mandatory POST parameters are:
+     *  - data: Received JSON file after a run.
+     *  - key: User API key.
+     *
+     * @category API
+     */
+
+    global $db;
+
+    try{
+        // Check data
+        $request = filter_input(INPUT_POST, 'request');
+        if ($request == null || $request == false){
+            http_response_code(400);
+            return 400;
+        }
+        $response = filter_input(INPUT_POST, 'response');
+        if ($response == null || $response == false){
+            http_response_code(400);
+            return 400;
+        }
+        // Check API key.
+        $key = filter_input(INPUT_POST, 'key');
+        if ($key == null || $key == false){
+            http_response_code(401);
+            return 401;
+        }
+        // Check data format.
+        $json_request = json_decode($request);
+        if ($json_request === null){
+            http_response_code(400);
+            return 400;
+        }
+        $json_response = json_decode($response);
+        if ($json_response === null){
+            http_response_code(400);
+            return 400;
+        }
+        // Authenticate
+        $uid = $json_request->{"wizard_id"};
+        $s = "SELECT COUNT(uid) AS c FROM player WHERE uid = $uid AND api_key = '$key';";
+        if (1 != $db->query($s)->fetchArray(SQLITE3_ASSOC)["c"]){
+            http_response_code(401);
+            return 401;
+        }
+
+        // Run scenario run parser script
+        $cmd = __DIR__ . "/bin/update_collection.py " . $key . " " . escapeshellarg($request). " " . escapeshellarg($response);
+        $out = [];
+        $ret = 0;
+        try{
+            exec($cmd, $out, $ret);
+        }
+        catch(Exception $e) {
+            error_log("Error running collection update script '$cmd': " . $e->getMessage());
+            http_response_code(500);
+            return 500;
+        }
+
+        if ($ret != 200){
+            try{
+                http_response_code($ret);
+                return $ret;
+            }
+            catch(Exception $e) {
+                error_log("Collection update script '$cmd' returned an unexpected value $ret: " . $e->getMessage());
+                http_response_code(500);
+                return 500;
+            }
+        }
+
+        // At this point, status code should be 200
+        http_response_code($ret);
+        return $ret;
+    }
+    catch(Exception $e) {
+        error_log("Unknown error updating collection: " . $e->getMessage());
+        http_response_code(500);
+        return 500;
+    }
+?>

+ 3 - 3
application/API/v2/update_logbook.php

@@ -48,7 +48,7 @@
             exec($cmd, $out, $ret);
         }
         catch(Exception $e) {
-            error_log("Error running scenario run script '$cmd': " . $e->getMessage());
+            error_log("Error running logbook update script '$cmd': " . $e->getMessage());
             http_response_code(500);
             return 500;
         }
@@ -59,7 +59,7 @@
                 return $ret;
             }
             catch(Exception $e) {
-                error_log("Dungeon run script '$cmd' returned an unexpected value $ret: " . $e->getMessage());
+                error_log("Logbook update script '$cmd' returned an unexpected value $ret: " . $e->getMessage());
                 http_response_code(500);
                 return 500;
             }
@@ -70,7 +70,7 @@
         return $ret;
     }
     catch(Exception $e) {
-        error_log("Unknown error parsing scenario run: " . $e->getMessage());
+        error_log("Unknown error parsing logbook update: " . $e->getMessage());
         http_response_code(500);
         return 500;
     }

+ 0 - 1
application/API/v2/upload_run_dimension.php

@@ -40,7 +40,6 @@
 
         // Authenticate
         $uid = $json_request->{"wizard_id"};
-        $uname = $json_response->{"wizard_info"}->{"wizard_name"};
         $s = "SELECT COUNT(uid) AS c FROM player WHERE uid = $uid AND api_key = '$key';";
         if (1 != $db->query($s)->fetchArray(SQLITE3_ASSOC)["c"]){
             http_response_code(401);

+ 0 - 1
application/API/v2/upload_run_dungeon.php

@@ -40,7 +40,6 @@
 
         // Authenticate
         $uid = $json_request->{"wizard_id"};
-        $uname = $json_response->{"wizard_info"}->{"wizard_name"};
         $s = "SELECT COUNT(uid) AS c FROM player WHERE uid = $uid AND api_key = '$key';";
         if (1 != $db->query($s)->fetchArray(SQLITE3_ASSOC)["c"]){
             http_response_code(401);

+ 0 - 1
application/API/v2/upload_run_rift.php

@@ -51,7 +51,6 @@
 
         // Authenticate
         $uid = $json_request->{"wizard_id"};
-        $uname = $json_response->{"wizard_info"}->{"wizard_name"};
         $s = "SELECT COUNT(uid) AS c FROM player WHERE uid = $uid AND api_key = '$key';";
         if (1 != $db->query($s)->fetchArray(SQLITE3_ASSOC)["c"]){
             http_response_code(401);

+ 0 - 1
application/API/v2/upload_run_scenario.php

@@ -37,7 +37,6 @@
 
         // Authenticate
         $uid = $json->{"wizard_id"};
-        $uname = $json->{"wizard_info"}->{"wizard_name"};
         $s = "SELECT COUNT(uid) AS c FROM player WHERE uid = $uid AND api_key = '$key';";
         if (1 != $db->query($s)->fetchArray(SQLITE3_ASSOC)["c"]){
             http_response_code(401);

+ 0 - 1
application/API/v2/upload_run_toa.php

@@ -40,7 +40,6 @@
 
         // Authenticate
         $uid = $json_request->{"wizard_id"};
-        $uname = $json_response->{"wizard_info"}->{"wizard_name"};
         $s = "SELECT COUNT(uid) AS c FROM player WHERE uid = $uid AND api_key = '$key';";
         if (1 != $db->query($s)->fetchArray(SQLITE3_ASSOC)["c"]){
             http_response_code(401);

+ 98 - 0
application/API/v3/API_Controller.php

@@ -0,0 +1,98 @@
+<?php
+
+    /**
+     * v3 API Controller file.
+     *
+     * Provides a class to handle all posible API requests.
+     *
+     * @category Constroller
+     */
+
+    /**
+     * v3 API controller.
+     *
+     * Handles every API request.
+     *
+     * @category Controller
+     */
+    class API_Controller {
+
+        /**
+         * Constructor.
+         *
+         * Handles every request, creating the required models and selecting the
+         * view.
+         *
+         * @param mixed[] $query GET parameters of the request.
+         */
+        public function __construct($query){
+
+            // Remove host
+            array_shift($query);
+            // Remove /API/
+            array_shift($query);
+            // Remove /v3/
+            array_shift($query);
+            // API call: Use API controller
+            $method = $_SERVER["REQUEST_METHOD"];
+            if (!in_array($method, ["GET", "POST", "PUT", "DELETE"])){
+                $method = "GET";
+            }
+            if (count($query) == 0 || $query[0] == null || $query[0] == ""){
+                require_once(__DIR__ . "/main.php");
+            }
+            else{
+                $command = strtolower($query[0]);
+                // Remove /<command>/
+                array_shift($query);
+                switch ($command){
+                    case "help":
+                        require_once(__DIR__ . "/help/index.php");
+                        break;
+                    case "profile":
+                    case "run":
+                    case "logbook":
+                    case "collection":
+                    case "run":
+                    case "units":
+                        if (file_exists(__DIR__ . "/$command/$method.php")){
+                            require_once(__DIR__ . "/$command/$method.php");
+                        }
+                        else{
+                            header("HTTP/1.1 404");
+                        }
+                        break;
+                    /*case "update_profile":
+                        require_once(__DIR__ . "/update_profile.php");
+                        break;
+                    case "update_logbook":
+                        require_once(__DIR__ . "/update_logbook.php");
+                        break;
+                    case "update_collection":
+                        require_once(__DIR__ . "/update_collection.php");
+                        break;
+                    case "log_run_dungeon":
+                        require_once(__DIR__ . "/log_run_dungeon.php");
+                        break;
+                    case "log_run_dh":
+                        require_once(__DIR__ . "/log_run_dh.php");
+                        break;
+                    case "log_run_scenario":
+                        require_once(__DIR__ . "/log_run_scenario.php");
+                        break;
+                    case "log_run_toa":
+                        require_once(__DIR__ . "/log_run_toa.php");
+                        break;
+                    case "log_run_rift_dungeon":
+                        require_once(__DIR__ . "/log_run_rift_dungeon.php");
+                        break;
+                    case "units":
+                        require_once(__DIR__ . "/units.php");
+                        break;*/
+                    default:
+                        header("HTTP/1.1 404");
+                }
+            }
+        }
+    }
+?>

+ 425 - 0
application/API/v3/help/api.php

@@ -0,0 +1,425 @@
+<?php
+
+    /**
+     * @var mixed[] $API API help structure.
+     */
+    $API = [
+        "version" => 2,
+        "category" => [
+            [ // BEGIN CATEGORY Units
+                "name" => "Units",
+                "command" => [
+                    [ // BEGIN COMMAND Units
+                        "name" => "units",
+                        "method" => [
+                            [
+                                "id" => "GET",
+                                "url" => "/units/",
+                                "description" => "Retrieves units.",
+                                "parameters" => [
+                                    [
+                                        "key" => "page",
+                                        "via" => "request",
+                                        "type" => "int",
+                                        "description" => "Page to fetch. Deaults to 1.",
+                                        "optional" => true
+                                    ],
+                                    [
+                                        "key" => "per_page",
+                                        "via" => "request",
+                                        "type" => "int",
+                                        "description" => "number of results per page. Defaults to 30, maximum is 100.",
+                                        "optional" => true
+                                    ],
+                                    [
+                                        "key" => "name",
+                                        "via" => "request",
+                                        "type" => "string",
+                                        "description" => "Filter units with this in their name. Case insensitive.",
+                                        "optional" => true
+                                    ]
+                                ],
+                                "responses" => [
+                                    [
+                                        "status" => "200",
+                                        "message" => "Success.",
+                                        "description" => null,
+                                        "example" => null,
+                                    ],
+                                ]
+                            ],
+                            [
+                                "id" => "GET",
+                                "url" => "/units/{id}/",
+                                "description" => "Retrieves a unit info.",
+                                "parameters" => [
+                                    [
+                                        "key" => "{id}",
+                                        "via" => "query",
+                                        "type" => "string",
+                                        "description" => "Unit ID.",
+                                        "optional" => false
+                                    ],
+                                ],
+                                "responses" => [
+                                    [
+                                        "status" => "200",
+                                        "message" => "Success.",
+                                        "description" => null,
+                                        "example" => null,
+                                    ],
+                                    [
+                                        "status" => "404",
+                                        "message" => "Unit not found.",
+                                        "description" => "The requested unit does not exist.",
+                                        "example" => null
+                                    ]
+                                ]
+                            ],
+                        ],
+                    ] // END COMMAND profile
+                ]
+            ], // END CATEGORY Units
+            [ // BEGIN CATEGORY Profile
+                "name" => "Profile",
+                "command" => [
+                    [ // BEGIN COMMAND UploadProfile
+                        "name" => "profile",
+                        "method" => [
+                            [
+                                "id" => "GET",
+                                "url" => "/profile/{id}/",
+                                "description" => "Retrieves player profile.",
+                                "parameters" => [
+                                    [
+                                        "key" => "{id}",
+                                        "via" => "query",
+                                        "type" => "string",
+                                        "description" => "Player ID or name.",
+                                        "optional" => false
+                                    ],
+                                    [
+                                        "key" => "key",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "API key. Required for private profiles.",
+                                        "optional" => true
+                                    ]
+                                ],
+                                "responses" => [
+                                    [
+                                        "status" => "200",
+                                        "message" => "Success.",
+                                        "description" => null,
+                                        "example" => json_encode('{"username":"IValentin","level":50,"public":"1"}', JSON_PRETTY_PRINT),
+                                    ],
+                                    [
+                                        "status" => "403",
+                                        "message" => "A list of profiles cant be retrieved. Please specify a profile ID or player name.",
+                                        "description" => "No player ID or name has been supplied. A full list of profiles is not available.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "403",
+                                        "message" => "The profile is not public.",
+                                        "description" => "The requestd player profile is private, and the API key has not been sent or it's invalid.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "404",
+                                        "message" => "Player not found.",
+                                        "description" => "The requestd player is not registered in SWDB.",
+                                        "example" => null
+                                    ]
+                                ]
+                            ],
+                            [
+                                "id" => "POST",
+                                "url" => "/profile/{id}/",
+                                "description" => "Creates a new player profile.",
+                                "parameters" => [
+                                    [
+                                        "key" => "{id}",
+                                        "via" => "query",
+                                        "type" => "string",
+                                        "description" => "Player ID.",
+                                        "optional" => false
+                                    ],
+                                    [
+                                        "key" => "email",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "User email. Must be a valid address.",
+                                        "optional" => false
+                                    ],
+                                    [
+                                        "key" => "password",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "New user password.",
+                                        "optional" => false
+                                    ],
+                                    [
+                                        "key" => "name",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "New username. If not supplied, it will default to 'player_[id]'. Will be overriden on the next PUT request.",
+                                        "optional" => true
+                                    ],
+                                    [
+                                        "key" => "public",
+                                        "via" => "body",
+                                        "type" => "int",
+                                        "description" => "1 to make the profile public, 0 to keep it private. Default to 0.",
+                                        "optional" => true
+                                    ],
+                                ],
+                                "responses" => [
+                                    [
+                                        "status" => "201",
+                                        "message" => "Created.",
+                                        "description" => null,
+                                        "example" => json_encode('{"username":"IValentin","email":example@example.com,"api_key":"0123456789012345","public":"0"}', JSON_PRETTY_PRINT),
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "User ID not received.",
+                                        "description" => "The ID of the player to create is not in the query.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "User email not received.",
+                                        "description" => "The email of the player to create has not ben passed in the request.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "Invalid email.",
+                                        "description" => "The 'mail' parameter must be a valid email address.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "User password not received.",
+                                        "description" => "The password of the player to create has not ben passed in the request.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "Existing user.",
+                                        "description" => "A player with the same ID is already registered in SWDB.",
+                                        "example" => null
+                                    ]
+                                ]
+                            ],
+                            [
+                                "id" => "PUT",
+                                "url" => "/profile/{id}/",
+                                "description" => "Updates a player profile with data received from the game.",
+                                "parameters" => [
+                                    [
+                                        "key" => "{id}",
+                                        "via" => "query",
+                                        "type" => "string",
+                                        "description" => "Player ID.",
+                                        "optional" => false
+                                    ],
+                                    [
+                                        "key" => "key",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "API key.",
+                                        "optional" => false
+                                    ],
+                                    [
+                                        "key" => "response",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "JSON received from game server on HubUserLogin command.",
+                                        "optional" => false
+                                    ]
+                                ],
+                                "responses" => [
+                                    [
+                                        "status" => "204",
+                                        "message" => "Profile imported.",
+                                        "description" => null,
+                                        "example" => null,
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "API key not received.",
+                                        "description" => "No API key was suplied in the request.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "No data received.",
+                                        "description" => "Game JSON response not received from request",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "Data is no valid JSON.",
+                                        "description" => "The parameter 'response' contains information, but it's not valid JSON data.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "401",
+                                        "message" => "Invalid credentials",
+                                        "description" => "The API key is not valid for the profile being updated.",
+                                        "example" => null
+                                    ]
+                                ]
+                            ],
+                            [
+                                "id" => "DELETE",
+                                "url" => "/profile/{id}/",
+                                "description" => "Deletes a player profile.",
+                                "parameters" => [
+                                    [
+                                        "key" => "{id}",
+                                        "via" => "query",
+                                        "type" => "string",
+                                        "description" => "Player ID.",
+                                        "optional" => false
+                                    ],
+                                    [
+                                        "key" => "key",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "API key.",
+                                        "optional" => false
+                                    ]
+                                ],
+                                "responses" => [
+                                    [
+                                        "status" => "204",
+                                        "message" => "Profile deleted.",
+                                        "description" => null,
+                                        "example" => null,
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "User ID not received.",
+                                        "description" => "The ID of the player to create is not in the query.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "API key not received.",
+                                        "description" => "No API key was suplied in the request.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "401",
+                                        "message" => "Invalid credentials",
+                                        "description" => "The API key is not valid for the profile being updated.",
+                                        "example" => null
+                                    ]
+                                ]
+                            ],
+                        ],
+                    ] // END COMMAND profile
+                ]
+            ], // END CATEGORY Profile
+            [ // BEGIN CATEGORY Run
+                "name" => "Run",
+                "command" => [
+                    [ // BEGIN COMMAND run
+                        "name" => "run",
+                        "method" => [
+                            [
+                                "id" => "POST",
+                                "url" => "/run/",
+                                "description" => "Logs a game run using JSON files intercepted from the game.",
+                                "parameters" => [
+                                    [
+                                        "key" => "key",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "API key.",
+                                        "optional" => false
+                                    ],
+                                    [
+                                        "key" => "result_response",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "JSON received from game server on run result.",
+                                        "optional" => false
+                                    ],
+                                    [
+                                        "key" => "result_request",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "JSON sent to game server on run result. Required for commands: BattleDungeonResult_V2, BattleDimensionHoleDungeonResult_v2",
+                                        "optional" => true
+                                    ],
+                                    [
+                                        "key" => "start_response",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "JSON received from game server on run start. Required for commands: BattleRiftOfWorldsRaidResult, BattleScenarioResult",
+                                        "optional" => true
+                                    ],
+                                    [
+                                        "key" => "start_request",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "JSON sent to game server on run start. Required for commands: ",
+                                        "optional" => true
+                                    ],
+                                ],
+                                "responses" => [
+                                    [
+                                        "status" => "201",
+                                        "message" => "Created.",
+                                        "description" => null,
+                                        "example" => null,
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "API key not received.",
+                                        "description" => "No API key was suplied in the request.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "No result response data received.",
+                                        "description" => "JSON received from game server on run result wast receiven on the request.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "Result response data is no valid JSON.",
+                                        "description" => "The parameter 'result_response' contains information, but it's not valid JSON data.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "401",
+                                        "message" => "Invalid credentials",
+                                        "description" => "The API key is not valid for the profile being updated.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "405",
+                                        "message" => "Command not implemented.",
+                                        "description" => "The command referenced in the JSON file is not implemented.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "409",
+                                        "message" => "Run already in database.",
+                                        "description" => "The run is already in the database for the user.",
+                                        "example" => null
+                                    ]
+                                ]
+                            ],
+                        ],
+                    ] // END COMMAND Run
+                ]
+            ], // END CATEGORY Run
+        ],
+    ];
+?>

+ 488 - 0
application/API/v3/help/index.php

@@ -0,0 +1,488 @@
+<?php
+    /**
+     * API Help view.
+     *
+     * Shows API documentation.
+     *
+     * @category View
+     * @var mixed[] $API API help structure.
+     */
+?>
+<?php
+    include __DIR__ . "/api.php";
+?>
+<!DOCTYPE html>
+<html lang='en'>
+    <head>
+        <meta content='text/html; charset=utf-8' http-equiv='content-type'/>
+        <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1'/>
+        <title>SWDB APIv3</title>
+        <link rel='shortcut icon' href='<?=URL::IMG["LOGO"]?>sw.png'/>
+        <!-- CSS -->
+        <link rel='stylesheet' type='text/css' href='<?=URL::CSS?>ui.css'/>
+        <style>
+
+            aside hr{
+                height: 0em;
+                padding: 0;
+                margin: 0 1em;
+                border: 0.01em solid #db490054;
+                border-radius: 1em;
+            }
+
+            aside h3 {
+                margin: 0.4em 0 0.2em 1em;
+                border-left: 0.5em solid #db490054;
+                border-radius: 0.2em;
+                padding-left: 0.3em;
+                background-color: #00000000;
+                transition: all .3s ease-in-out;
+            }
+
+            aside h3:hover{
+                background-color: #db490054;
+            }
+
+            aside h3 a{
+                color: #cccccc
+            }
+
+            aside h3:hover a{
+                color: #ffffff
+            }
+
+            aside ul {
+                margin: 0.2em 0;
+                list-style: none;
+            }
+
+            aside ul li{
+                border-left: 0.5em solid #db490054;
+                border-radius: 0.2em;
+                padding-left: 0.3em;
+                margin-bottom: 0.2em;
+                background-color: #00000000;
+                transition: all .3s ease-in-out;
+            }
+
+            aside ul li:hover{
+                background-color: #db490054;
+            }
+
+            aside ul li a{
+                color: #cccccc
+            }
+
+            aside ul li:hover a{
+                color: #ffffff
+            }
+
+            aside div#copyright{
+                text-align: center;
+                font-size: 70%;
+                position: absolute;
+                bottom: 1em;
+            }
+
+            section.category article{
+                margin: 1.5em;
+                padding-left: 1em;
+                padding-right: 1em;
+                text-align: left;
+            }
+
+            section.category article h3{
+                vertical-align: bottom;
+            }
+
+            section.category article h3 span.description{
+                font-style: italic;
+                font-size: 90%;
+            }
+
+            section.category article:not(:last-child){
+                padding-bottom: 1em;
+                margin-bottom: 1em;
+                border-bottom: 0.1em solid #ffffff66;
+            }
+
+            section.category article div.command span{
+                display: inline-block;
+                border-radius: 0.6em;
+                padding: 0.3em 0.5em;
+                border-width: 0.15em;
+                border-style: solid;
+                color: #ffffff;
+                font-family: monospace, monospace;
+                font-weight: bold;
+            }
+
+            section.category article div.command span.url{
+                background-color: #292b36;
+                border-color: #4b4d57;
+            }
+
+            section.category article div.command span.type{
+                text-transform: uppercase;
+            }
+
+            section.category article div.command span.type_GET {
+                background-color: #008000;
+                border-color: #22a222;
+            }
+
+            section.category article div.command span.type_PUT {
+                background-color: #e5c500;
+                border-color: #f7e722;
+            }
+
+            section.category article div.command span.type_POST {
+                background-color: #4070ec;
+                border-color: #6292fe;
+            }
+
+            section.category article div.command span.type_DELETE {
+                background-color: #ed0039;
+                border-color: #ff225b;
+            }
+
+            section.category article table{
+                border-collapse: collapse;
+                width: 80%;
+                border-radius: 0.5em;
+                margin: 0.5em auto 1em auto;
+            }
+
+            section.category article table th {
+                background-color: #331100;
+                text-align: left;
+                font-weight: bold;
+                padding: 0.5em 1em;
+                border: 0.1px solid #e0e0e0;
+            }
+
+            section.category article table td {
+                background-color: #352413;
+                vertical-align: top;
+                padding: 0.2em 0.4em;
+                border: #e0e0e0 1px solid;
+            }
+
+            section.category article table td.name {
+                font-family: monospace, monospace;
+                font-weight: bold;
+            }
+
+            section.category article table td.name span.location{
+                padding-left: 1em;
+                font-size: 80%;
+                font-style: italic;
+                color: #ffffff77;
+            }
+
+            section.category article table td.type {
+                font-family: monospace, monospace;
+                font-weight: bold;
+            }
+
+            section.category article table td.status span{
+                display: inline-block;
+                border-radius: 0.6em;
+                padding: 0.3em 0.5em;
+                border-width: 0.15em;
+                border-style: solid;
+                color: #ffffff;
+                font-family: monospace, monospace;
+                font-weight: bold;
+            }
+
+            section.category article table td.status_1 span{
+                border-color: #999999;
+                background-color: #777777;
+            }
+            section.category article table td.status_2 span{
+                background-color: #008000;
+                border-color: #22a222;
+            }
+            section.category article table td.status_3 span{
+                background-color: #4070ec;
+                border-color: #6292fe;
+            }
+            section.category article table td.status_4 span{
+                background-color: #e5c500;
+                border-color: #f7e722;
+            }
+            section.category article table td.status_5 span{
+                background-color: #ed0039;
+                border-color: #ff225b;
+            }
+
+            section.category article table td.message{
+                font-family: monospace, monospace;
+            }
+
+            section.category article table td.description span.optional {
+                font-size: 90%;
+                color: #bbbbbb;
+                font-style: italic;
+            }
+
+            section#example{
+                position: fixed;
+                display: none;
+                top: 15%;
+                left: 20%;
+                width: 60%;
+                max-width: 90em;
+                height: 70%;
+                max-height: 30em;
+                text-align: right;
+            }
+
+            section#example article{
+                text-align: right;
+            }
+
+            section#example article pre#example_content{
+                text-align: left;
+                background-color: #292b36;
+                border: 0.3em solid #4b4d57;
+                border-radius: 0.8em;
+                padding: 1em;
+                max-height: 20em;
+                overflow: scroll;
+            }
+
+        </style>
+        <!-- Javascript -->
+        <script>
+            function showExample(command, status, example){
+                document.getElementById('example_title').innerHTML = command + " HTTP status " + status + " response example";
+                document.getElementById('example_content').innerHTML = JSON.stringify(JSON.parse(example), null, 4);
+                document.getElementById('example').style.display = 'block';
+            }
+            function closeExample(command, status, example){
+                document.getElementById('example').style.display = 'none';
+            }
+        </script>
+        <!-- Meta tags -->
+        <link rel='canonical' href='<?=URL::BASE?>API/v3/help/'/>
+        <link rel='author' href='SWDB'/>
+        <link rel='publisher' href='SWDB'/>
+        <meta name='description' content='SWDB API v3 Documentation'/>
+        <meta property='og:title' content='SWDB APIv3'/>
+        <meta property='og:url' content='<?=URL::BASE?>API/v3/help/'/>
+        <meta property='og:description' content='SWDB API v3 Documentation'/>
+        <meta property='og:image' content='<?=URL::IMG["LOGO"]?>sw.png'/>
+        <meta property='og:site_name' content='SWDB'/>
+        <meta property='og:type' content='website'/>
+        <meta property='og:locale' content='en'/>
+        <meta name='twitter:card' content='summary'/>
+        <meta name='twitter:title' content='SWDB APIv3'/>
+        <meta name='twitter:description' content='SWDB API v3 Documentation'/>
+        <meta name='twitter:image' content='<?=URL::IMG["LOGO"]?>sw.png'/>
+        <meta name='twitter:url' content='<?=URL::BASE?>API/v3/help/'/>
+        <meta name='robots' content='index follow'/>
+    </head>
+    <body>
+        <header>
+            <div id='logo'>
+                <img id='logo_img' src='<?=URL::IMG["LOGO"]?>sw.png' alt=''/>
+                <h1 id='logo_text'>
+                    <span class='large'>D</span>
+                    <span class='small'>ata</span>
+                    <span class='large'>B</span>
+                    <span class='small'>ase</span>
+                </h1>
+            </div>
+        </header>
+        <aside>
+            <h2>
+                API v3 endopoints:
+            </h2>
+<?php
+            foreach ($API["category"] as $category){
+?>
+                <h3>
+                    <a href='#category_<?=$category["name"]?>'>
+                        <?=$category["name"]?>
+                    </a>
+                </h3>
+                <ul>
+<?php
+                    foreach ($category["command"] as $command){
+                        foreach ($command["method"] as $method){
+?>
+                            <li>
+                                <a href='#command_<?=$command["name"]?>-<?=$method["id"]?>-<?=htmlspecialchars($method["url"])?>' title='<?=$command["description"]?>'>
+                                    <?=$method["id"]?> <?=htmlspecialchars($method["url"])?>
+                                </a>
+                            </li>
+<?php
+                        }
+                    }
+?>
+                </ul>
+                <hr/>
+<?php
+            }
+?>
+        </aside>
+        <main>
+<?php
+            foreach ($API["category"] as $category){
+                foreach ($category["command"] as $command){
+?>
+                    <section id='category_<?=$category["name"]?>' class='category content'>
+                    <h2>
+                        <?=$category["name"]?>
+                    </h2>
+                        <article id='command_<?=$command["name"]?>'>
+                            <h3>
+                                <?=$command["name"]?>
+                            </h3>
+<?php
+                            foreach ($command["method"] as $method){
+?>
+                                <div class='command' id='command_<?=$command["name"]?>-<?=$method["id"]?>-<?=htmlspecialchars($method["url"])?>'>
+                                    <span class='type type_<?=$method["id"]?>'>
+                                        <?=$method["id"]?>
+                                    </span>
+                                    <span class='url'>
+                                        <?=htmlspecialchars($method["url"])?>
+                                    </span>
+                                    <span class='description'>
+                                        <?=$method["description"]?>
+                                    </span>
+                                </div>
+                                <table class='params'>
+                                    <thead>
+                                        <tr>
+                                            <th class='header' colspan='3'>
+                                                Parameters
+                                            </th>
+                                        </tr>
+                                        <tr>
+                                            <th class='name'>
+                                                Parameter
+                                            </th>
+                                            <th class='type'>
+                                                Type
+                                            </th>
+                                            <th class='description'>
+                                                Description
+                                            </th>
+                                        </tr>
+                                    </thead>
+                                    <tbody>
+    <?php
+                                        foreach ($method["parameters"] as $param){
+    ?>
+                                            <tr>
+                                                <td class='name'>
+                                                    <?=$param["key"]?>
+                                                    <span class='location'>
+                                                        (<?=$param["via"]?>)
+                                                    </span>
+                                                </td>
+                                                <td class='type'>
+                                                    <?=$param["type"]?>
+                                                </td>
+                                                <td class='description'>
+                                                    <?=($param["optional"] ? "<span class='optional'>[Optional] </span>" : "")?>
+                                                    <?=$param["description"]?>
+                                                </td>
+                                            </tr>
+    <?php
+                                        }
+    ?>
+                                    </tbody>
+                                </table>
+                                <table class='responses'>
+                                    <thead>
+                                        <tr>
+                                            <th class='header' colspan='3'>
+                                                Responses
+                                            </th>
+                                        </tr>
+                                        <tr>
+                                            <th class='status'>
+                                                Status
+                                            </th>
+                                            <th class='status'>
+                                                Message
+                                            </th>
+                                            <th class='description'>
+                                                Description
+                                            </th>
+                                        </tr>
+                                    </thead>
+                                    <tbody>
+    <?php
+                                        foreach ($method["responses"] as $response){
+    ?>
+                                            <tr>
+                                                <td class='status status_<?=substr($response["status"], 0, 1)?>'>
+                                                    <span>
+                                                        <?=$response["status"]?>
+                                                    </span>
+                                                </td>
+                                                <td class='message'>
+                                                    <?=$response["message"]?>
+                                                </td>
+                                                <td class='description'>
+                                                    <?=$response["description"]?>
+    <?php
+                                                    if ($response["example"] != null){
+    ?>
+                                                        <span class='fake_a' onclick="showExample('<?=$command["name"]?>', <?=$response["status"]?>, '<?=htmlspecialchars(json_decode($response["example"]), ENT_QUOTES)?>')">
+                                                            View example.
+                                                        </span>
+    <?php
+                                                    }
+    ?>
+                                                </td>
+                                            </tr>
+    <?php
+                                        }
+    ?>
+                                    </tbody>
+                                </table>
+<?php
+                            }
+?>
+                        </article>
+                    </section>
+<?php
+                }
+?>
+                <section id='example' class='content'>
+                    <h2 id='example_title'>
+                    </h2>
+                    <article>
+                        <pre id='example_content'>
+                        </pre>
+                        <input type='button' value='Close' onClick='closeExample();'>
+                    </article>
+                </section>
+<?php
+            }
+?>
+        </main>
+        <footer>
+            <table id='footer_table'>
+                <tr>
+                    <td id='footer_left'>
+                        Version <?=APP::VERSION?>. Developed by <a href='https://inigovalentin.com'>I&ntilde;igo Valentin</a>.
+                        <br/>
+                        <br/>
+                        Source code available on <a href='https://github.com'>Github</a> under the GPLv3.
+                    </td>
+                    <td id='footer_right'>
+                        Neither I nor this tool are affiliated with or endorsed by <a href='http://com2us.com/'>Com2uS</a> in any way. The Summoners >
+                    </td>
+                </tr>
+            </table>
+        </footer>
+    </body>
+</html>

+ 17 - 0
application/API/v3/main.php

@@ -0,0 +1,17 @@
+<?php
+    header("Content-Type:application/json");
+    $links = [
+        "Archetypes" => URL::BASE . "/API/v2/archetypes",
+        "Area types" => URL::BASE . "/API/v2/area_types",
+        "Areas types" => URL::BASE . "/API/v2/areas",
+        "Buildings" => URL::BASE . "/API/v2/buildings",
+        "Decorations" => URL::BASE . "/API/v2/decorations",
+        "Areas types" => URL::BASE . "/API/v2/areas",
+        "Units" => URL::BASE . "/API/v2/units",
+        "Skills" => URL::BASE . "/API/v2/skills",
+        "Skill effects" => URL::BASE . "/API/v2/skill_efects",
+    ];
+    header("HTTP/1.1 200");
+    $json_response = json_encode($links);
+    echo $json_response;
+?>

+ 77 - 0
application/API/v3/profile/DELETE.php

@@ -0,0 +1,77 @@
+<?php
+    /**
+     * Profile deleter script.
+     *
+     * Exposes an API to update an existing profile.
+     *
+     * It also saves the data to a JSON file in the data directory.
+     *
+     * Mandatory query parameters are:
+     *  - id: Player ID.
+     *
+     * Mandatory DELETE parameters are:
+     *  - key: User API key.
+     *
+     * @category API
+     */
+
+    global $db;
+
+    try{
+
+        header("Content-type: application/json; charset=utf-8");
+
+        // Get put data
+        $_DELETE = [];
+        parse_str(file_get_contents("php://input"), $_DELETE);
+
+        // Get profile ID
+        /** @var mixed $query Request parameters, from API_CONTROLLER*/
+        if (count($query) > 0){
+            $id = $query[0];
+        }
+        else{
+            // ID is mandatory
+            header("HTTP/1.1 400 User ID not received.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 User ID not received.");
+            return 400;
+        }
+
+        // Check API key.
+        $api_key = $_DELETE["key"];
+        if ($api_key == null || $api_key == false){
+            header("HTTP/1.1 400 API key not received.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 API key not received.");
+            return 400;
+        }
+
+        // Authenticate
+        $statement = $db->prepare("SELECT COUNT(uid) AS c FROM player WHERE uid = :uid AND api_key = :api_key;");
+        $statement->bindValue(":uid", $id, SQLITE3_INTEGER);
+        $statement->bindValue(":api_key", $api_key, SQLITE3_TEXT);
+        if (1 != $statement->execute()->fetchArray(SQLITE3_ASSOC)["c"]){
+            header("HTTP/1.1 401 Invalid credentials.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 401 Invalid credentials.");
+            return 401;
+        }
+
+        $statement = $db->prepare("
+          DELETE FROM player
+          WHERE
+            uid = :uid AND
+            api_key, = :api_key;
+        ");
+        $statement->bindValue(":uid", $id, SQLITE3_INTEGER);
+        $statement->bindValue(":api_key", $api_key, SQLITE3_TEXT);
+        $statement->execute();
+
+        header("HTTP/1.1 204 Profile deleted.");
+        syslog(LOG_INFO, "[APIv3] HTTP/1.1 204 Profile deleted.");
+        return 204;
+    }
+    catch(Exception $e) {
+        header("HTTP/1.1 500 Unexpeced error: " . $e->getMessage());
+        syslog(LOG_ERR, "[APIv3] HTTP/1.1 500 Unexpected error: " . $e->getMessage());
+        return 500;
+    }
+?>

+ 67 - 0
application/API/v3/profile/GET.php

@@ -0,0 +1,67 @@
+<?php
+    /**
+     * Profile getter script.
+     *
+     * Exposes an API to rerieve a user profile.
+     *
+     * A lst of profiles can't be retrieved.
+     *
+     * Mandatory query parameters are:
+     *  - id: Player ID.
+     *
+     * Optional GET parameters are.
+     *  - key: User API key to retrieve a private profile.
+     *
+     * @category API
+     */
+
+    global $db;
+
+    try{
+        header("Content-type: application/json; charset=utf-8");
+
+        // Get profile ID
+        /** @var mixed $query Request parameters, from API_CONTROLLER*/
+        if (count($query) > 1){
+            $id = $query[0];
+        }
+        else{
+            // Id is mandatory, a list can be retrieved
+            header("HTTP/1.1 403 A list of profiles cant be retrieved. Please specify a profile ID or player name.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 403 A list of profiles cant be retrieved. Please specify a profile ID or player name.");
+            return 403;
+        }
+
+        // Get player
+        $statement = $db->prepare("SELECT * FROM player WHERE uid = :uid OR upper(name) = upper(:uid);");
+        $statement->bindValue(":uid", $id, SQLITE3_INTEGER);
+        $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
+        if ($r == null){
+            // No player found
+            header("HTTP/1.1 404 Player not found.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 404 Player not found.");
+            return 403;
+        }
+        if ($r["public"] != 1 && $r["key"] != filter_input(INPUT_GET, "key")){
+            header("HTTP/1.1 403 The profile is not public.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 403 The profile is not public.");
+            return 403;
+        }
+
+        // Build array
+        $player = [
+            "username" => $r["name"],
+            "level" => $r["level"],
+            "public" => "1"
+        ];
+        echo(json_encode($player));
+        header("HTTP/1.1 200 Success.");
+        syslog(LOG_INFO, "[APIv3] HTTP/1.1 200 Success.");
+        return 200;
+    }
+    catch(Exception $e) {
+        header("HTTP/1.1 500 Unexpeced error: " . $e->getMessage());
+        syslog(LOG_ERR, "[APIv3] HTTP/1.1 500 Unexpected error: " . $e->getMessage());
+        return 500;
+    }
+?>

+ 211 - 0
application/API/v3/profile/POST.php

@@ -0,0 +1,211 @@
+<?php
+    /**
+     * Profile creator script.
+     *
+     * Exposes an API to create a new profile.
+     *
+     * Mandatory query parameters are:
+     *  - id: Player ID.
+     *
+     * Mandatory POST parameters are:
+     *  - email: User email.
+     *  - password: User password.
+     *
+     * Optional POST parameters are:
+     *  - name: User name.
+     *  - public: 1 To make the profile public, 0 for private.
+     *
+     * @category API
+     */
+
+    global $db;
+
+    try{
+
+        header("Content-type: application/json; charset=utf-8");
+
+        // Get put data
+        parse_str(file_get_contents("php://input"), $_POST);
+
+        // Get profile ID
+        /** @var mixed $query Request parameters, from API_CONTROLLER*/
+        if (count($query) > 0){
+            $id = $query[0];
+        }
+        else{
+            // ID is mandatory
+            header("HTTP/1.1 400 User ID not received.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 User ID not received.");
+            return 400;
+        }
+
+        // Check user mail.
+        $mail = $_POST["email"];
+        if ($mail == null || $mail == false){
+            header("HTTP/1.1 400 User email not received.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 User email not received.");
+            return 400;
+        }
+        if (!filter_var($mail, FILTER_VALIDATE_EMAIL)) {
+            header("HTTP/1.1 400 Invalid email.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 Invalid email.");
+            return 400;
+        }
+        // Check user password.
+        $password = $_POST["password"];
+        if ($password == null || $password == false){
+            header("HTTP/1.1 400 User password not received.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 User password not received.");
+            return 400;
+        }
+        // Check user name.
+        $name = $_POST["name"];
+        if ($name == null || $name == false){
+            $name = "player_" . $id;
+        }
+        // Check user name.
+        $public = intval($_POST["public"]);
+        if ($public != 1){
+            $public = 0;
+        }
+
+        // Check if player exists
+        $statement = $db->prepare("SELECT COUNT(uid) AS c FROM player WHERE uid = :uid;");
+        $statement->bindValue(":uid", $id, SQLITE3_INTEGER);
+        if (1 != $statement->execute()->fetchArray(SQLITE3_ASSOC)["c"]){
+            header("HTTP/1.1 400 Existing user.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 Existing user.");
+            return 400;
+        }
+
+        // Prepare inputs
+        $password = hash("sha256", $password);
+        $characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        $charactersLength = 16;
+        $api_key = "";
+        for ($i = 0; $i < $charactersLength; $i++) {
+            $api_key .= $characters[rand(0, $charactersLength - 1)];
+        }
+
+        // Insert
+        $statement = $db->prepare("
+          INSERT INTO player (
+            uid,
+            name,
+            mail,
+            password,
+            api_key,
+            mana,
+            crystal,
+            country,
+            lang,
+            level,
+            experience,
+            energy,
+            energy_max,
+            energy_per_min,
+            arena_energy,
+            arena_energy_max,
+            rep,
+            social_point,
+            honor_point,
+            guild_point,
+            darkportal_energy,
+            darkportal_energy_max,
+            dimension_energy,
+            dimension_energy_max,
+            costume_point,
+            costume_point_max,
+            honor_medal,
+            honor_mark,
+            event_coin,
+            storage_slots,
+            island,
+            public
+          ) VALUES (
+            :uid,
+            :name,
+            :mail,
+            :password,
+            :api_key,
+            :mana,
+            :crystal,
+            :country,
+            :lang,
+            :level,
+            :experience,
+            :energy,
+            :energy_max,
+            :energy_per_min,
+            :arena_energy,
+            :arena_energy_max,
+            :rep,
+            :social_point,
+            :honor_point,
+            :guild_point,
+            :darkportal_energy,
+            :darkportal_energy_max,
+            :dimension_energy,
+            :dimension_energy_max,
+            :costume_point,
+            :costume_point_max,
+            :honor_medal,
+            :honor_mark,
+            :event_coin,
+            :storage_slots,
+            :island,
+            :public
+          );
+        ");
+        $statement->bindValue(":uid", $id, SQLITE3_INTEGER);
+        $statement->bindValue(":name", $name, SQLITE3_TEXT);
+        $statement->bindValue(":mail", $mail, SQLITE3_TEXT);
+        $statement->bindValue(":password", $password, SQLITE3_TEXT);
+        $statement->bindValue(":api_key", $api_key, SQLITE3_TEXT);
+        $statement->bindValue(":mana", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":crystal", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":country", null, SQLITE3_TEXT);
+        $statement->bindValue(":lang", null, SQLITE3_TEXT);
+        $statement->bindValue(":level", 1, SQLITE3_INTEGER);
+        $statement->bindValue(":experience", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":energy", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":energy_max", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":energy_per_min", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":arena_energy", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":arena_energy_max", 10, SQLITE3_INTEGER);
+        $statement->bindValue(":rep", null, SQLITE3_INTEGER);
+        $statement->bindValue(":social_point", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":honor_point", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":guild_point", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":darkportal_energy", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":darkportal_energy_max", 10, SQLITE3_INTEGER);
+        $statement->bindValue(":dimension_energy", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":dimension_energy_max", 100, SQLITE3_INTEGER);
+        $statement->bindValue(":costume_point", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":costume_point_max", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":honor_medal", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":honor_mark", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":event_coin", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":storage_slots", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":island_upgrade", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":public", $public, SQLITE3_INTEGER);
+        $statement->execute();
+
+        // Build array
+        $player = [
+            "username" => $name,
+            "email" => $mail,
+            "api_key" => $api_key,
+            "public" => "1"
+        ];
+        echo(json_encode($player));
+        header("HTTP/1.1 201 Created.");
+        syslog(LOG_INFO, "[APIv3] HTTP/1.1 201 Created.");
+        return 201;
+    }
+    catch(Exception $e) {
+        header("HTTP/1.1 500 Unexpeced error: " . $e->getMessage());
+        syslog(LOG_ERR, "[APIv3] HTTP/1.1 500 Unexpected error: " . $e->getMessage());
+        return 500;
+    }
+?>

+ 1659 - 0
application/API/v3/profile/PUT.php

@@ -0,0 +1,1659 @@
+<?php
+    /**
+     * Profile uploader script.
+     *
+     * Exposes an API to update an existing profile.
+     *
+     * It also saves the data to a JSON file in the data directory.
+     *
+     * Mandatory PUT parameters are:
+     *  - response: JSON received from game server on HubUserLogin command.
+     *  - key: User API key.
+     *
+     * @category API
+     */
+
+    global $db;
+
+    /**
+     * Calculates the currrent efficiency of a rune.
+     *
+     * @param mixed[] $rune JSON fragment of the rune.
+     * @return int Efficiency (0-100)
+     */
+    function calculate_efficiency($rune){
+        $efficiency = 0.0;
+        $level = $rune->{"upgrade_curr"};
+        $stars = $rune->{"class"};
+        // Ancient stars are + 10
+        if ($stars > 10){
+            $stars -= 10;
+        }
+        $list = null;
+        // Main stat
+        $main_stat = $rune->{"pri_eff"}[0];
+        switch ($main_stat){
+            case RUNE_STAT_ID::HP:
+                $list = RUNE_STAT_VALUES::HP;
+                break;
+            case RUNE_STAT_ID::HP_P:
+                $list = RUNE_STAT_VALUES::HP_P;
+                break;
+            case RUNE_STAT_ID::ATK:
+                $list = RUNE_STAT_VALUES::ATK;
+                break;
+            case RUNE_STAT_ID::ATK_P:
+                $list = RUNE_STAT_VALUES::ATK_P;
+                break;
+            case RUNE_STAT_ID::DEF:
+                $list = RUNE_STAT_VALUES::DEF;
+                break;
+            case RUNE_STAT_ID::DEF_P:
+                $list = RUNE_STAT_VALUES::DEF_P;
+                break;
+            case RUNE_STAT_ID::SPD:
+                $list = RUNE_STAT_VALUES::SPD;
+                break;
+            case RUNE_STAT_ID::CRR:
+                $list = RUNE_STAT_VALUES::CRR;
+                break;
+            case RUNE_STAT_ID::CRD:
+                $list = RUNE_STAT_VALUES::CRD;
+                break;
+            case RUNE_STAT_ID::ACC:
+                $list = RUNE_STAT_VALUES::ACC;
+                break;
+            case RUNE_STAT_ID::RES:
+                $list = RUNE_STAT_VALUES::RES;
+                break;
+        }
+        $efficiency += floatval($list[$stars][$level]) / floatval($list[6][15]);
+        // Innate stat
+        if ($rune->{"prefix_eff"}[0] > 0){
+            $innate_stat = $rune->{"pri_eff"}[0];
+            $innate_stat_value = $rune->{"pri_eff"}[1];
+            $efficiency += floatval($innate_stat_value) / (5 * floatval(RUNE_SUBSTAT_MAX_VALUES::SUBSTAT[$innate_stat][6]));
+        }
+        // Substats
+        for ($i = 0; $i <= 3; $i ++){
+            if (sizeof($rune->{"sec_eff"}) > $i){
+                $substat = $rune->{"sec_eff"}[$i][0];
+                $substat_value = $rune->{"sec_eff"}[$i][1];
+                $efficiency += floatval($substat_value) / (5 * floatval(RUNE_SUBSTAT_MAX_VALUES::SUBSTAT[$substat][6]));
+            }
+        }
+        $efficiency = $efficiency / 2.8 * 100;
+        if ($efficiency > 100){
+            $efficiency = 100;
+        }
+        return $efficiency;
+    }
+
+    /**
+     * Calculates the maximum efficiency of a rune.
+     *
+     * @param mixed[] $rune JSON fragment of the rune.
+     * @return int Maximum efficiency (0-100)
+     */
+    function calculate_maximum_efficiency($rune){
+        $efficiency = 0.0;
+        $stars = $rune->{"class"};
+        // Ancient stars are + 10
+        if ($stars > 10){
+            $stars -= 10;
+        }
+        $list = null;
+        // Main stat
+        $main_stat = $rune->{"pri_eff"}[0];
+        switch ($main_stat){
+            case RUNE_STAT_ID::HP:
+                $list = RUNE_STAT_VALUES::HP;
+                break;
+            case RUNE_STAT_ID::HP_P:
+                $list = RUNE_STAT_VALUES::HP_P;
+                break;
+            case RUNE_STAT_ID::ATK:
+                $list = RUNE_STAT_VALUES::ATK;
+                break;
+            case RUNE_STAT_ID::ATK_P:
+                $list = RUNE_STAT_VALUES::ATK_P;
+                break;
+            case RUNE_STAT_ID::DEF:
+                $list = RUNE_STAT_VALUES::DEF;
+                break;
+            case RUNE_STAT_ID::DEF_P:
+                $list = RUNE_STAT_VALUES::DEF_P;
+                break;
+            case RUNE_STAT_ID::SPD:
+                $list = RUNE_STAT_VALUES::SPD;
+                break;
+            case RUNE_STAT_ID::CRR:
+                $list = RUNE_STAT_VALUES::CRR;
+                break;
+            case RUNE_STAT_ID::CRD:
+                $list = RUNE_STAT_VALUES::CRD;
+                break;
+            case RUNE_STAT_ID::ACC:
+                $list = RUNE_STAT_VALUES::ACC;
+                break;
+            case RUNE_STAT_ID::RES:
+                $list = RUNE_STAT_VALUES::RES;
+                break;
+        }
+        $efficiency += floatval($list[$stars][15]) / floatval($list[6][15]);
+        // Innate stat
+        if ($rune->{"prefix_eff"}[0] > 0){
+            $innate_stat = $rune->{"pri_eff"}[0];
+            $innate_stat_value = $rune->{"pri_eff"}[1];
+            $efficiency += floatval($innate_stat_value) / (5 * floatval(RUNE_SUBSTAT_MAX_VALUES::SUBSTAT[$innate_stat][6]));
+        }
+        // Substats
+        for ($i = 0; $i <= 3; $i ++){
+            if (sizeof($rune->{"sec_eff"}) > $i){
+                $substat = $rune->{"sec_eff"}[$i][0];
+                $efficiency += floatval(RUNE_SUBSTAT_MAX_VALUES::SUBSTAT[$substat][$stars]) / (5 * floatval(RUNE_SUBSTAT_MAX_VALUES::SUBSTAT[$substat][6]));
+            }
+        }
+        $efficiency = $efficiency / 2.8 * 100;
+        if ($efficiency > 100){
+            $efficiency = 100;
+        }
+        return $efficiency;
+    }
+    
+    /**
+     * Parses an artifact and saves it to the database.
+     *
+     * @param string $uid Owner ID.
+     * @param JSON artifact JSON fragment of the artifact.
+     */
+    function parse_artifact($uid, $artifact){
+        global $db;
+        $statement = $db->prepare("
+          INSERT INTO artifact (
+            uid,
+            id,
+            assigned_to,
+            type,
+            attribute,
+            archetype,
+            level,
+            quality,
+            original_quality,
+            value,
+            efficiency,
+            max_efficiency,
+            main_stat,
+            main_stat_value,
+            effect_1,
+            effect_1_value,
+            effect_1_enchant,
+            effect_1_grind,
+            effect_2,
+            effect_2_value,
+            effect_2_enchant,
+            effect_2_grind,
+            effect_3,
+            effect_3_value,
+            effect_3_enchant,
+            effect_3_grind,
+            effect_4,
+            effect_4_value,
+            effect_4_enchant,
+            effect_4_grind,
+            locked
+          ) VALUES (
+            :uid,
+            :id,
+            :assigned_to,
+            :type,
+            :attribute,
+            :archetype,
+            :level,
+            :quality,
+            :original_quality,
+            :value,
+            :efficiency,
+            :max_efficiency,
+            :main_stat,
+            :main_stat_value,
+            :effect_1,
+            :effect_1_value,
+            :effect_1_enchant,
+            :effect_1_grind,
+            :effect_2,
+            :effect_2_value,
+            :effect_2_enchant,
+            :effect_2_grind,
+            :effect_3,
+            :effect_3_value,
+            :effect_3_enchant,
+            :effect_3_grind,
+            :effect_4,
+            :effect_4_value,
+            :effect_4_enchant,
+            :effect_4_grind,
+            :locked
+          );
+        ");
+        $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+        $statement->bindValue(":id", $artifact->{"rid"}, SQLITE3_INTEGER);
+        if (intval($artifact->{"occupied_id"}) == 0){
+            $statement->bindValue(":assigned_to", null, SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":assigned_to", $artifact->{"occupied_id"}, SQLITE3_INTEGER);
+        }
+        if (intval($artifact->{"attribute"}) == 0){
+            $statement->bindValue(":attribute", null, SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":attribute", $artifact->{"attribute"}, SQLITE3_INTEGER);
+        }
+        if (intval($artifact->{"type"}) == 0){
+            $statement->bindValue(":type", null, SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":type", $artifact->{"type"}, SQLITE3_INTEGER);
+        }
+        if (intval($artifact->{"unit_style"}) == 0){
+            $statement->bindValue(":archetype", null, SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":archetype", $artifact->{"unit_style"}, SQLITE3_INTEGER);
+        }
+        $statement->bindValue(":level", $artifact->{"level"}, SQLITE3_INTEGER);
+        $statement->bindValue(":quality", $artifact->{"rank"}, SQLITE3_INTEGER);
+        $statement->bindValue(":original_quality", $artifact->{"natural_rank"}, SQLITE3_INTEGER);
+        $statement->bindValue(":value", 0, SQLITE3_INTEGER); // Not in JSON file
+        $statement->bindValue(":locked", $artifact->{"locked"}, SQLITE3_INTEGER);
+        $efficiency = 0; // Not standard formula yet
+        $max_efficiency = 0; // Not standard formula yet
+        $statement->bindValue(":efficiency", $efficiency, SQLITE3_FLOAT);
+        $statement->bindValue(":max_efficiency", $max_efficiency, SQLITE3_FLOAT);
+        $statement->bindValue(":main_stat", $artifact->{"pri_effect"}[0], SQLITE3_INTEGER);
+        $statement->bindValue(":main_stat_value", $artifact->{"pri_effect"}[1], SQLITE3_INTEGER);
+        if (sizeof($artifact->{"sec_effects"}) > 0){
+            $statement->bindValue(":effect_1", $artifact->{"sec_effects"}[0][0], SQLITE3_INTEGER);
+            $statement->bindValue(":effect_1_value", $artifact->{"sec_effects"}[0][1], SQLITE3_INTEGER);
+            $statement->bindValue(":effect_1_enchant", $artifact->{"sec_effects"}[0][2], SQLITE3_INTEGER);
+            $statement->bindValue(":effect_1_grind", $artifact->{"sec_effects"}[0][3], SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":effect_1", null, SQLITE3_INTEGER);
+            $statement->bindValue(":effect_1_value", 0, SQLITE3_INTEGER);
+            $statement->bindValue(":effect_1_enchant", 0, SQLITE3_INTEGER);
+            $statement->bindValue(":effect_1_grind", 0, SQLITE3_INTEGER);
+        }
+        if (sizeof($artifact->{"sec_effects"}) > 1){
+            $statement->bindValue(":effect_2", $artifact->{"sec_effects"}[1][0], SQLITE3_INTEGER);
+            $statement->bindValue(":effect_2_value", $artifact->{"sec_effects"}[1][1], SQLITE3_INTEGER);
+            $statement->bindValue(":effect_2_enchant", $artifact->{"sec_effects"}[1][2], SQLITE3_INTEGER);
+            $statement->bindValue(":effect_2_grind", $artifact->{"sec_effects"}[1][3], SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":effect_2", null, SQLITE3_INTEGER);
+            $statement->bindValue(":effect_2_value", 0, SQLITE3_INTEGER);
+            $statement->bindValue(":effect_2_enchant", 0, SQLITE3_INTEGER);
+            $statement->bindValue(":effect_2_grind", 0, SQLITE3_INTEGER);
+        }
+        if (sizeof($artifact->{"sec_effects"}) > 2){
+            $statement->bindValue(":effect_3", $artifact->{"sec_effects"}[2][0], SQLITE3_INTEGER);
+            $statement->bindValue(":effect_3_value", $artifact->{"sec_effects"}[2][1], SQLITE3_INTEGER);
+            $statement->bindValue(":effect_3_enchant", $artifact->{"sec_effects"}[2][2], SQLITE3_INTEGER);
+            $statement->bindValue(":effect_3_grind", $artifact->{"sec_effects"}[2][3], SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":effect_3", null, SQLITE3_INTEGER);
+            $statement->bindValue(":effect_3_value", 0, SQLITE3_INTEGER);
+            $statement->bindValue(":effect_3_enchant", 0, SQLITE3_INTEGER);
+            $statement->bindValue(":effect_3_grind", 0, SQLITE3_INTEGER);
+        }
+        if (sizeof($artifact->{"sec_effects"}) > 3){
+            $statement->bindValue(":effect_4", $artifact->{"sec_effects"}[3][0], SQLITE3_INTEGER);
+            $statement->bindValue(":effect_4_value", $artifact->{"sec_effects"}[3][1], SQLITE3_INTEGER);
+            $statement->bindValue(":effect_4_enchant", $artifact->{"sec_effects"}[3][2], SQLITE3_INTEGER);
+            $statement->bindValue(":effect_4_grind", $artifact->{"sec_effects"}[3][3], SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":effect_4", null, SQLITE3_INTEGER);
+            $statement->bindValue(":effect_4_value", 0, SQLITE3_INTEGER);
+            $statement->bindValue(":effect_4_enchant", 0, SQLITE3_INTEGER);
+            $statement->bindValue(":effect_4_grind", 0, SQLITE3_INTEGER);
+        }
+        $statement->execute();
+    }
+
+    /**
+     * Parses a rune and saves it to the database.
+     *
+     * @param string $uid Owner ID.
+     * @param JSON rune JSON fragment of the rune.
+     */
+    function parse_rune($uid, $rune, $lock_list){
+        global $db;
+        $statement = $db->prepare("
+          INSERT INTO rune (
+            uid,
+            id,
+            assigned_to,
+            type,
+            slot,
+            stars,
+            ancient,
+            level,
+            quality,
+            original_quality,
+            value,
+            efficiency,
+            max_efficiency,
+            main_stat,
+            main_stat_value,
+            innate_stat,
+            innate_stat_value,
+            substat_1,
+            substat_1_value,
+            substat_1_enchant,
+            substat_1_grind,
+            substat_2,
+            substat_2_value,
+            substat_2_enchant,
+            substat_2_grind,
+            substat_3,
+            substat_3_value,
+            substat_3_enchant,
+            substat_3_grind,
+            substat_4,
+            substat_4_value,
+            substat_4_enchant,
+            substat_4_grind,
+            locked
+          ) VALUES (
+            :uid,
+            :id,
+            :assigned_to,
+            :type,
+            :slot,
+            :stars,
+            :ancient,
+            :level,
+            :quality,
+            :original_quality,
+            :value,
+            :efficiency,
+            :max_efficiency,
+            :main_stat,
+            :main_stat_value,
+            :innate_stat,
+            :innate_stat_value,
+            :substat_1,
+            :substat_1_value,
+            :substat_1_enchant,
+            :substat_1_grind,
+            :substat_2,
+            :substat_2_value,
+            :substat_2_enchant,
+            :substat_2_grind,
+            :substat_3,
+            :substat_3_value,
+            :substat_3_enchant,
+            :substat_3_grind,
+            :substat_4,
+            :substat_4_value,
+            :substat_4_enchant,
+            :substat_4_grind,
+            :locked
+          );
+        ");
+        $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+        $statement->bindValue(":id", $rune->{"rune_id"}, SQLITE3_INTEGER);
+        if (intval($rune->{"occupied_id"}) == 0){
+            $statement->bindValue(":assigned_to", null, SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":assigned_to", $rune->{"occupied_id"}, SQLITE3_INTEGER);
+        }
+        $statement->bindValue(":type", $rune->{"set_id"}, SQLITE3_INTEGER);
+        $statement->bindValue(":slot", $rune->{"slot_no"}, SQLITE3_INTEGER);
+        $statement->bindValue(":value", $rune->{"sell_value"}, SQLITE3_INTEGER);
+        if ($rune->{"class"} > 10){
+            // Ancient rune
+            $statement->bindValue(":ancient", 1, SQLITE3_INTEGER);
+            $statement->bindValue(":stars", intval($rune->{"class"}) - 10, SQLITE3_INTEGER);
+            $statement->bindValue(":original_quality", intval($rune->{"extra"}) - 10, SQLITE3_INTEGER);
+        }
+        else{
+            // Non Ancient rune
+            $statement->bindValue(":ancient", 0, SQLITE3_INTEGER);
+            $statement->bindValue(":stars", $rune->{"class"}, SQLITE3_INTEGER);
+            $statement->bindValue(":original_quality", $rune->{"extra"}, SQLITE3_INTEGER);
+        }
+        $level = $rune->{"upgrade_curr"};
+        $statement->bindValue(":level", $level, SQLITE3_INTEGER);
+        $quality = QUALITY_ID::NORMAL;
+        if ($level >= 12){
+            $quality = QUALITY_ID::LEGEND;
+        }
+        elseif ($level >= 9){
+            $quality = QUALITY_ID::HERO;
+        }
+        elseif ($level >= 6){
+            $quality = QUALITY_ID::RARE;
+        }
+        elseif ($level >= 3){
+            $quality = QUALITY_ID::MAGIC;
+        }
+        if ($rune->{"extra"} > $quality){
+            $quality = $rune->{"extra"};
+        }
+        $statement->bindValue(":quality", $quality, SQLITE3_INTEGER);
+        $lock = 0;
+        if (in_array($rune->{"rune_id"}, $lock_list)){
+            $lock = 1;
+        }
+        $statement->bindValue(":locked", $lock, SQLITE3_INTEGER);
+        $efficiency = calculate_efficiency($rune);
+        $max_efficiency = calculate_maximum_efficiency($rune);
+        $statement->bindValue(":efficiency", $efficiency, SQLITE3_FLOAT);
+        $statement->bindValue(":max_efficiency", $max_efficiency, SQLITE3_FLOAT);
+        $statement->bindValue(":main_stat", $rune->{"pri_eff"}[0], SQLITE3_INTEGER);
+        $statement->bindValue(":main_stat_value", $rune->{"pri_eff"}[1], SQLITE3_INTEGER);
+        if ($rune->{"prefix_eff"}[0] > 0){
+            $statement->bindValue(":innate_stat", $rune->{"prefix_eff"}[0], SQLITE3_INTEGER);
+            $statement->bindValue(":innate_stat_value", $rune->{"prefix_eff"}[1], SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":innate_stat", null, SQLITE3_INTEGER);
+            $statement->bindValue(":innate_stat_value", 0, SQLITE3_INTEGER);
+        }
+        if (sizeof($rune->{"sec_eff"}) > 0){
+            $statement->bindValue(":substat_1", $rune->{"sec_eff"}[0][0], SQLITE3_INTEGER);
+            $statement->bindValue(":substat_1_value", $rune->{"sec_eff"}[0][1], SQLITE3_INTEGER);
+            $statement->bindValue(":substat_1_enchant", $rune->{"sec_eff"}[0][2], SQLITE3_INTEGER);
+            $statement->bindValue(":substat_1_grind", $rune->{"sec_eff"}[0][3], SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":substat_1", null, SQLITE3_INTEGER);
+            $statement->bindValue(":substat_1_value", 0, SQLITE3_INTEGER);
+            $statement->bindValue(":substat_1_enchant", 0, SQLITE3_INTEGER);
+            $statement->bindValue(":substat_1_grind", 0, SQLITE3_INTEGER);
+        }
+        if (sizeof($rune->{"sec_eff"}) > 1){
+            $statement->bindValue(":substat_2", $rune->{"sec_eff"}[1][0], SQLITE3_INTEGER);
+            $statement->bindValue(":substat_2_value", $rune->{"sec_eff"}[1][1], SQLITE3_INTEGER);
+            $statement->bindValue(":substat_2_enchant", $rune->{"sec_eff"}[1][2], SQLITE3_INTEGER);
+            $statement->bindValue(":substat_2_grind", $rune->{"sec_eff"}[1][3], SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":substat_2", null, SQLITE3_INTEGER);
+            $statement->bindValue(":substat_2_value", 0, SQLITE3_INTEGER);
+            $statement->bindValue(":substat_2_enchant", 0, SQLITE3_INTEGER);
+            $statement->bindValue(":substat_2_grind", 0, SQLITE3_INTEGER);
+        }
+        if (sizeof($rune->{"sec_eff"}) > 2){
+            $statement->bindValue(":substat_3", $rune->{"sec_eff"}[2][0], SQLITE3_INTEGER);
+            $statement->bindValue(":substat_3_value", $rune->{"sec_eff"}[2][1], SQLITE3_INTEGER);
+            $statement->bindValue(":substat_3_enchant", $rune->{"sec_eff"}[2][2], SQLITE3_INTEGER);
+            $statement->bindValue(":substat_3_grind", $rune->{"sec_eff"}[2][3], SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":substat_3", null, SQLITE3_INTEGER);
+            $statement->bindValue(":substat_3_value", 0, SQLITE3_INTEGER);
+            $statement->bindValue(":substat_3_enchant", 0, SQLITE3_INTEGER);
+            $statement->bindValue(":substat_3_grind", 0, SQLITE3_INTEGER);
+        }
+        if (sizeof($rune->{"sec_eff"}) > 3){
+            $statement->bindValue(":substat_4", $rune->{"sec_eff"}[3][0], SQLITE3_INTEGER);
+            $statement->bindValue(":substat_4_value", $rune->{"sec_eff"}[3][1], SQLITE3_INTEGER);
+            $statement->bindValue(":substat_4_enchant", $rune->{"sec_eff"}[3][2], SQLITE3_INTEGER);
+            $statement->bindValue(":substat_4_grind", $rune->{"sec_eff"}[3][3], SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":substat_4", null, SQLITE3_INTEGER);
+            $statement->bindValue(":substat_4_value", 0, SQLITE3_INTEGER);
+            $statement->bindValue(":substat_4_enchant", 0, SQLITE3_INTEGER);
+            $statement->bindValue(":substat_4_grind", 0, SQLITE3_INTEGER);
+        }
+        $statement->execute();
+    }
+
+    /**
+     * Parses the response to the HubUserLogin command and updates profile.
+     *
+     * @param resource $db Database connection.
+     * @param int $uid Player UID.
+     * @param mixed[] $json Response.
+     */
+    function parse_profile($db, $uid, $json){
+        // Save data file
+        $uname = $json->{"wizard_info"}->{"wizard_name"};
+        $dtime = (new DateTime())->format("Y-m-dTH:i:s");
+        $fname = ($_SERVER["DOCUMENT_ROOT"] . "/../data/profile_" . $uid . "_" . $uname . "_" . $dtime . ".json");
+        try{
+            file_put_contents($fname, json_encode($json));
+        }
+        catch(Exception $e) {
+            header("HTTP/1.1 500 Unable to save profile file: " . $e->getMessage());
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 500 Unable to save profile file: " . $e->getMessage());
+            return 500;
+        }
+
+        // Clear previous profile data
+        $db->query("PRAGMA foreign_keys = OFF;");
+        $statement = $db->prepare("DELETE FROM unit_skill WHERE unit IN (SELECT id FROM unit WHERE uid = :uid);");
+        $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+        $statement->execute();
+        $tables_to_clear = [
+            "scenario",
+            "defense",
+            "gw_defense",
+            "unit",
+            "rune",
+            "artifact",
+            "building",
+            "decoration",
+            "inventory",
+            "enchantment"
+        ];
+        foreach ($tables_to_clear as $table){
+            $statement = $db->prepare("DELETE FROM $table WHERE uid = :uid;");
+            $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+            $statement->execute();
+        }
+        $db->query("DELETE FROM summon_special"); # For every player, will be reloaded.
+
+        // Get rune lock list
+        $rune_lock_list = $json->{"rune_lock_list"};
+
+        // Update player data
+        $statement = $db->prepare("
+          UPDATE player
+          SET
+            name = :name,
+            mana = :mana,
+            crystal = :crystal,
+            country = :country,
+            lang = :lang,
+            level = :level,
+            experience = :experience,
+            energy = :energy,
+            energy_max = :energy_max,
+            energy_per_min = :energy_per_min,
+            arena_energy = :arena_energy,
+            arena_energy_max = :arena_energy_max,
+            rep = :rep,
+            social_point = :social_point,
+            honor_point = :honor_point,
+            guild_point = :guild_point,
+            darkportal_energy = :darkportal_energy,
+            darkportal_energy_max = :darkportal_energy_max,
+            dimension_energy = :dimension_energy,
+            dimension_energy_max = :dimension_energy_max,
+            costume_point = :costume_point,
+            costume_point_max = :costume_point_max,
+            honor_medal = :honor_medal,
+            honor_mark = :honor_mark,
+            event_coin = :event_coin,
+            storage_slots = :storage_slots,
+            island = :island_upgrade
+          WHERE uid = :uid;
+        ");
+        $statement->bindValue(":name", $json->{"wizard_info"}->{"wizard_name"}, SQLITE3_TEXT);
+        $statement->bindValue(":mana", $json->{"wizard_info"}->{"wizard_mana"}, SQLITE3_INTEGER);
+        $statement->bindValue(":crystal", $json->{"wizard_info"}->{"wizard_crystal"}, SQLITE3_INTEGER);
+        $statement->bindValue(":country", $json->{"wizard_info"}->{"wizard_last_country"}, SQLITE3_TEXT);
+        $statement->bindValue(":lang", $json->{"wizard_info"}->{"wizard_last_lang"}, SQLITE3_TEXT);
+        $statement->bindValue(":level", $json->{"wizard_info"}->{"wizard_level"}, SQLITE3_INTEGER);
+        $statement->bindValue(":experience", $json->{"wizard_info"}->{"experience"}, SQLITE3_INTEGER);
+        $statement->bindValue(":energy", $json->{"wizard_info"}->{"wizard_energy"}, SQLITE3_INTEGER);
+        $statement->bindValue(":energy_max", $json->{"wizard_info"}->{"energy_max"}, SQLITE3_INTEGER);
+        $statement->bindValue(":energy_per_min", $json->{"wizard_info"}->{"energy_per_min"}, SQLITE3_INTEGER);
+        $statement->bindValue(":arena_energy", $json->{"wizard_info"}->{"arena_energy"}, SQLITE3_INTEGER);
+        $statement->bindValue(":arena_energy_max", $json->{"wizard_info"}->{"arena_energy_max"}, SQLITE3_INTEGER);
+        $statement->bindValue(":rep", $json->{"wizard_info"}->{"rep_unit_id"}, SQLITE3_INTEGER);
+        $statement->bindValue(":social_point", $json->{"wizard_info"}->{"social_point_current"}, SQLITE3_INTEGER);
+        $statement->bindValue(":honor_point", $json->{"wizard_info"}->{"honor_point"}, SQLITE3_INTEGER);
+        $statement->bindValue(":guild_point", $json->{"wizard_info"}->{"guild_point"}, SQLITE3_INTEGER);
+        $statement->bindValue(":darkportal_energy", $json->{"wizard_info"}->{"darkportal_energy"}, SQLITE3_INTEGER);
+        $statement->bindValue(":darkportal_energy_max", $json->{"wizard_info"}->{"darkportal_energy_max"}, SQLITE3_INTEGER);
+        $statement->bindValue(":dimension_energy", $json->{"dimension_hole_info"}->{"energy"}, SQLITE3_INTEGER);
+        $statement->bindValue(":dimension_energy_max", $json->{"dimension_hole_info"}->{"energy_max"}, SQLITE3_INTEGER);
+        $statement->bindValue(":costume_point", $json->{"wizard_info"}->{"costume_point"}, SQLITE3_INTEGER);
+        $statement->bindValue(":costume_point_max", $json->{"wizard_info"}->{"costume_point_max"}, SQLITE3_INTEGER);
+        $statement->bindValue(":honor_medal", $json->{"wizard_info"}->{"honor_medal"}, SQLITE3_INTEGER);
+        $statement->bindValue(":honor_mark", $json->{"wizard_info"}->{"honor_mark"}, SQLITE3_INTEGER);
+        $statement->bindValue(":event_coin", $json->{"wizard_info"}->{"event_coin"}, SQLITE3_INTEGER);
+        $statement->bindValue(":storage_slots", $json->{"unit_depository_slots"}->{"number"}, SQLITE3_INTEGER);
+        $island_upgrade = 0;
+        foreach ($json->{"island_info"} as $island){
+            if (intval($island->{"id"}) > 100 && intval($island->{"open"}) == 1){
+                $island_upgrade = intval($island->{"id"});
+            }
+        }
+        $statement->bindValue(":island_upgrade", $island_upgrade, SQLITE3_INTEGER);
+        $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+        $statement->execute();
+
+        // Update scenarion completion
+        foreach ($json->{"scenario_list"} as $sce){
+            $statement = $db->prepare("
+              INSERT INTO scenario (
+                uid,
+                region,
+                difficulty,
+                cleared,
+                max_cleared
+              ) VALUES (
+                :uid,
+                :region,
+                :difficulty,
+                :cleared,
+                :max_cleared
+              );
+            ");
+            $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+            $statement->bindValue(":region", $sce->{"region_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":difficulty", $sce->{"difficulty"}, SQLITE3_INTEGER);
+            $statement->bindValue(":cleared", $sce->{"cleared"}, SQLITE3_INTEGER);
+            $max_cleared = 0;
+            foreach ($sce->{"stage_list"} as $stage){
+                if (intval($stage->{"cleared"}) == 1){
+                    $max_cleared = intval($stage->{"stage_no"});
+                }
+            }
+            $statement->bindValue(":max_cleared", $max_cleared, SQLITE3_INTEGER);
+            $statement->execute();
+        }
+
+        // Parse Arena defense
+        foreach ($json->{"defense_unit_list"} as $defense){
+            $statement = $db->prepare("
+              INSERT INTO defense (
+                uid,
+                unit,
+                position
+              ) VALUES (
+                :uid,
+                :unit,
+                :position
+              );
+            ");
+            $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+            $statement->bindValue(":unit", $defense->{"unit_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":position", $defense->{"pos_id"}, SQLITE3_INTEGER);
+            $statement->execute();
+        }
+
+        // Parse GW defense
+        if ($json->{"guildwar_defense_unit_list"}[0]){
+            $position = 1;
+            foreach ($json->{"guildwar_defense_unit_list"}[0] as $defense){
+                $statement = $db->prepare("
+                  INSERT INTO gw_defense (
+                    uid,
+                    unit,
+                    position
+                  ) VALUES (
+                    :uid,
+                    :unit,
+                    :position
+                  );
+                ");
+                $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+                $statement->bindValue(":unit", $defense->{"unit_id"}, SQLITE3_INTEGER);
+                $statement->bindValue(":position", $position, SQLITE3_INTEGER);
+                $statement->execute();
+                $position ++;
+            }
+        }
+        if ($json->{"guildwar_defense_unit_list"}[1]){
+            $position = 4;
+            foreach ($json->{"guildwar_defense_unit_list"}[1] as $defense){
+                $statement = $db->prepare("
+                  INSERT INTO gw_defense (
+                    uid,
+                    unit,
+                    position
+                  ) VALUES (
+                    :uid,
+                    :unit,
+                    :position
+                  );
+                ");
+                $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+                $statement->bindValue(":unit", $defense->{"unit_id"}, SQLITE3_INTEGER);
+                $statement->bindValue(":position", $position, SQLITE3_INTEGER);
+                $statement->execute();
+                $position ++;
+            }
+        }
+
+        // Parse buildings
+        foreach($json->{"building_list"} as $building){
+            $statement = $db->prepare("
+              INSERT INTO building (
+                uid,
+                id,
+                building,
+                gain
+              ) VALUES (
+                :uid,
+                :id,
+                :building,
+                :gain
+              );
+            ");
+            $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+            $statement->bindValue(":id", $building->{"building_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":building", $building->{"building_master_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":gain", $building->{"gain_per_hour"}, SQLITE3_INTEGER);
+            $statement->execute();
+        }
+
+        // Parse decorarion
+        foreach($json->{"deco_list"} as $building){
+            $statement = $db->prepare("
+              INSERT INTO decoration (
+                uid,
+                id,
+                decoration,
+                level
+              ) VALUES (
+                :uid,
+                :id,
+                :decoration,
+                :level
+              );
+            ");
+            $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+            $statement->bindValue(":id", $building->{"deco_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":decoration", $building->{"master_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":level", $building->{"level"}, SQLITE3_INTEGER);
+            $statement->execute();
+        }
+
+        // Parse units
+        $lock_list = $json->{"unit_lock_list"};
+        foreach ($json->{"unit_list"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO unit (
+                uid,
+                id,
+                building,
+                unit,
+                level,
+                stars,
+                hp,
+                attack,
+                defense,
+                speed,
+                crit_rate,
+                crit_damage,
+                resistance,
+                accuracy,
+                experience,
+                exp_gained,
+                exp_gain_rate,
+                costume,
+                attribute,
+                source,
+                create_time,
+                homunculus,
+                homunculus_name,
+                lock
+              ) VALUES (
+                :uid,
+                :id,
+                :building,
+                :unit,
+                :level,
+                :stars,
+                :hp,
+                :attack,
+                :defense,
+                :speed,
+                :crit_rate,
+                :crit_damage,
+                :resistance,
+                :accuracy,
+                :experience,
+                :exp_gained,
+                :exp_gain_rate,
+                :costume,
+                :attribute,
+                :source,
+                :create_time,
+                :homunculus,
+                :homunculus_name,
+                :lock
+              );
+            ");
+            $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+            $statement->bindValue(":id", $unit->{"unit_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":building", $unit->{"building_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":unit", $unit->{"unit_master_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":level", $unit->{"unit_level"}, SQLITE3_INTEGER);
+            $statement->bindValue(":stars", $unit->{"class"}, SQLITE3_INTEGER);
+            $statement->bindValue(":hp", intval($unit->{"con"}) * 15, SQLITE3_INTEGER); # Always x15
+            $statement->bindValue(":attack", $unit->{"atk"}, SQLITE3_INTEGER);
+            $statement->bindValue(":defense", $unit->{"def"}, SQLITE3_INTEGER);
+            $statement->bindValue(":speed", $unit->{"spd"}, SQLITE3_INTEGER);
+            $statement->bindValue(":crit_rate", $unit->{"critical_rate"}, SQLITE3_INTEGER);
+            $statement->bindValue(":crit_damage", $unit->{"critical_damage"}, SQLITE3_INTEGER);
+            $statement->bindValue(":resistance", $unit->{"resist"}, SQLITE3_INTEGER);
+            $statement->bindValue(":accuracy", $unit->{"accuracy"}, SQLITE3_INTEGER);
+            $statement->bindValue(":experience", $unit->{"experience"}, SQLITE3_INTEGER);
+            $statement->bindValue(":exp_gained", $unit->{"exp_gained"}, SQLITE3_INTEGER);
+            $statement->bindValue(":exp_gain_rate", $unit->{"exp_gain_rate"}, SQLITE3_INTEGER);
+            $statement->bindValue(":costume", $unit->{"costume_master_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":attribute", $unit->{"attribute"}, SQLITE3_INTEGER);
+            $statement->bindValue(":source", $unit->{"source"}, SQLITE3_INTEGER);
+            $statement->bindValue(":create_time", $unit->{"create_time"}, SQLITE3_INTEGER);
+            $statement->bindValue(":homunculus", $unit->{"homunculus"}, SQLITE3_INTEGER);
+            $statement->bindValue(":homunculus_name", $unit->{"homunculus_name"}, SQLITE3_TEXT);
+            $lock = 0;
+            if (in_array($unit->{"unit_id"}, $lock_list)){
+                $lock = 1;
+            }
+            $statement->bindValue(":lock", $lock, SQLITE3_INTEGER);
+            $statement->execute();
+            foreach ($unit->{"skills"} as $skill){
+                $statement = $db->prepare("
+                  INSERT INTO unit_skill (
+                    unit,
+                    skill,
+                    level
+                  ) VALUES (
+                    :unit,
+                    :skill,
+                    :level
+                  );
+                ");
+                $statement->bindValue(":unit", $unit->{"unit_id"}, SQLITE3_INTEGER);
+                $statement->bindValue(":skill", $skill[0], SQLITE3_INTEGER);
+                $statement->bindValue(":level", $skill[1], SQLITE3_INTEGER);
+                $statement->execute();
+            }
+        }
+
+        // Parse invetory
+        foreach ($json->{"inventory_info"} as $item){
+            $statement = $db->prepare("
+              INSERT INTO inventory (
+                uid,
+                id,
+                type,
+                amount
+              ) VALUES (
+                :uid,
+                :id,
+                :type,
+                :amount
+              );
+            ");
+            $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+            $statement->bindValue(":id", $item->{"item_master_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":type", $item->{"item_master_type"}, SQLITE3_INTEGER);
+            $statement->bindValue(":amount", $item->{"item_quantity"}, SQLITE3_INTEGER);
+            $statement->execute();
+        }
+        // Fix rune craft item type.
+        $db->query("UPDATE inventory SET type = 27 WHERE type = 29 AND id IN (2001, 4001, 4002, 4003, 9001, 9002, 9003, 8001);");
+
+        // Parse Summon Stone summoning list
+        // TODO: Set propper dates
+        foreach ($json->{"summon_special_info"}->{"this"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO summon_special (
+                week,
+                unit
+              ) VALUES (
+                :week,
+                :unit
+              );
+            ");
+            $statement->bindValue(":week", 0, SQLITE3_INTEGER);
+            $statement->bindValue(":unit", $unit, SQLITE3_INTEGER);
+            $statement->execute();
+        }
+        foreach ($json->{"summon_special_info"}->{"next"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO summon_special (
+                week,
+                unit
+              ) VALUES (
+                :week,
+                :unit
+              );
+            ");
+            $statement->bindValue(":week", 1, SQLITE3_INTEGER);
+            $statement->bindValue(":unit", $unit, SQLITE3_INTEGER);
+            $statement->execute();
+        }
+        foreach ($json->{"summon_special_info"}->{"third"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO summon_special (
+                week,
+                unit
+              ) VALUES (
+                :week,
+                :unit
+              );
+            ");
+            $statement->bindValue(":week", 2, SQLITE3_INTEGER);
+            $statement->bindValue(":unit", $unit, SQLITE3_INTEGER);
+            $statement->execute();
+        }
+        foreach ($json->{"summon_special_info"}->{"fourth"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO summon_special (
+                week,
+                unit
+              ) VALUES (
+                :week,
+                :unit
+              );
+            ");
+            $statement->bindValue(":week", 3, SQLITE3_INTEGER);
+            $statement->bindValue(":unit", $unit, SQLITE3_INTEGER);
+            $statement->execute();
+        }
+
+        // Parse runes
+        foreach ($json->{"runes"} as $rune){
+            parse_rune($uid, $rune, $rune_lock_list);
+        }
+        foreach ($json->{"unit_list"} as $unit){
+            foreach ($unit->{"runes"} as $rune){
+                parse_rune($uid, $rune, $rune_lock_list);
+            }
+        }
+
+        // Parse artifacts
+        foreach ($json->{"artifacts"} as $artifact){
+            parse_artifact($uid, $artifact);
+        }
+        foreach ($json->{"unit_list"} as $unit){
+            foreach ($unit->{"artifacts"} as $artifact){
+                parse_artifact($uid, $artifact);
+            }
+        }
+
+        // Parse enchantments
+        foreach ($json->{"rune_craft_item_list"} as $item){
+            $statement = $db->prepare("
+              INSERT INTO enchantment (
+                uid,
+                id,
+                type,
+                quality,
+                rune,
+                stat,
+                value,
+                amount
+              ) VALUES (
+                :uid,
+                :id,
+                :type,
+                :quality,
+                :rune,
+                :stat,
+                :value,
+                :amount
+              );
+            ");
+            $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+            $statement->bindValue(":id", $item->{"craft_item_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":type", $item->{"craft_type"}, SQLITE3_INTEGER);
+            $statement->bindValue(":value", $item->{"sell_value"}, SQLITE3_INTEGER);
+            $statement->bindValue(":amount", $item->{"amount"}, SQLITE3_INTEGER);
+            $info = str_pad(intval($item->{"craft_type_id"}), 6, "0", STR_PAD_LEFT);
+            /*
+            info : 5 or 6 digit number: RRSSQQ
+             RR: Rune (may not be padded)
+             SS: Stat
+             QQ:  Quality
+            */
+            $statement->bindValue(":quality", intval(substr($info, 4, 2)), SQLITE3_INTEGER);
+            $statement->bindValue(":stat", intval(substr($info, 2, 2)), SQLITE3_INTEGER);
+            $statement->bindValue(":rune", intval(substr($info, 0, 2)), SQLITE3_INTEGER);
+            $statement->execute();
+        }
+
+        // Parse guild
+        if ($json->{"guild"}->{"guild_info"} != null){
+            $guild = $json->{"guild"}->{"guild_info"};
+            $statement = $db->prepare("DELETE FROM guild WHERE id = :id;");
+            $statement->bindValue(":id", $guild->{"guild_id"}, SQLITE3_INTEGER);
+            $statement->execute();
+            $statement = $db->prepare("DELETE FROM guild_member WHERE guild = :guild;");
+            $statement->bindValue(":guild", $guild->{"guild_id"}, SQLITE3_INTEGER);
+            $statement->execute();
+            $statement = $db->prepare("
+              INSERT INTO guild (
+                id,
+                name,
+                level,
+                experience,
+                recruiting,
+                members,
+                leader,
+                comment,
+                notice
+              ) VALUES (
+                :id,
+                :name,
+                :level,
+                :experience,
+                :recruiting,
+                :members,
+                :leader,
+                :comment,
+                :notice
+              );
+            ");
+            $statement->bindValue(":id",$guild->{"guild_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":name",$guild->{"name"}, SQLITE3_TEXT);
+            $statement->bindValue(":level",$guild->{"level"}, SQLITE3_INTEGER);
+            $statement->bindValue(":experience",$guild->{"experience"}, SQLITE3_INTEGER);
+            $statement->bindValue(":recruiting",$guild->{"recruit_status"}, SQLITE3_INTEGER);
+            $statement->bindValue(":members",$guild->{"member_now"}, SQLITE3_INTEGER);
+            $statement->bindValue(":leader",$guild->{"master_wizard_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":comment",$guild->{"comment"}, SQLITE3_TEXT);
+            $statement->bindValue(":notice",$guild->{"notice"}, SQLITE3_TEXT);
+            $statement->execute();
+            foreach ($json->{"guild"}->{"guild_members"} as $member){
+                $statement = $db->prepare("
+                  INSERT INTO guild_member (
+                    id,
+                    guild,
+                    grade,
+                    name,
+                    level,
+                    rating,
+                    arena_score,
+                    joined,
+                    last_login,
+                    in_war,
+                    has_defense
+                  ) VALUES (
+                    :id,
+                    :guild,
+                    :grade,
+                    :name,
+                    :level,
+                    :rating,
+                    :arena_score,
+                    :joined,
+                    :last_login,
+                    :in_war,
+                    :has_defense
+                  );
+                ");
+                $statement->bindValue(":id", $member->{"wizard_id"}, SQLITE3_INTEGER);
+                $statement->bindValue(":guild", $member->{"guild_id"}, SQLITE3_INTEGER);
+                $statement->bindValue(":grade", $member->{"grade"}, SQLITE3_INTEGER);
+                $statement->bindValue(":name", $member->{"wizard_name"}, SQLITE3_TEXT);
+                $statement->bindValue(":level", $member->{"wizard_level"}, SQLITE3_INTEGER);
+                $statement->bindValue(":rating", $member->{"rating_id"}, SQLITE3_INTEGER);
+                $statement->bindValue(":arena_score", $member->{"arena_score"}, SQLITE3_INTEGER);
+                $statement->bindValue(":joined", $member->{"join_timestamp"}, SQLITE3_TEXT);
+                $statement->bindValue(":last_login", $member->{"last_login_timestamp"}, SQLITE3_TEXT);
+                $in_war = 0;
+                foreach ($json->{"guildwar_member_list"} as $war){
+                    if ($war->{"wizard_id"} == $member->{"wizard_id"}){
+                        $in_war = 1;
+                        break;
+                    }
+                }
+                $has_defense = 0;
+                foreach ($json->{"guild_member_defense_list"} as $defense){
+                    if ($defense->{"wizard_id"} == $member->{"wizard_id"}){
+                        $has_defense = 1;
+                        break;
+                    }
+                }
+                $statement->bindValue(":in_war", $in_war, SQLITE3_INTEGER);
+                $statement->bindValue(":has_defense", $has_defense, SQLITE3_INTEGER);
+                $statement->execute();
+            }
+        }
+
+        // Update invalid teams
+        $statement = $db->prepare("
+          DELETE FROM team 
+          WHERE 
+            id IN (
+              SELECT DISTINCT team 
+              FROM team_unit 
+              WHERE unit NOT IN (
+                SELECT DISTINCT id FROM unit WHERE uid = :uid
+              )
+            ) AND
+            uid = :uid
+          ");
+        $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+        $statement->execute();
+        $db->query("
+          DELETE FROM team_unit
+          WHERE 
+            unit NOT IN (
+              SELECT DISTINCT id FROM unit
+            ) OR 
+            team NOT IN (
+              SELECT DISTINCT id FROM team
+            );"
+        );
+        // Return success
+        return "201 Created.";
+    }
+
+    /**
+     * Parses the response to the GetUnitCollection command and updates the
+     * user monster collection status.
+     *
+     * @param resource $db Database connection.
+     * @param int $uid Player UID.
+     * @param mixed[] $json Response.
+     */
+    function parse_collection($db, $uid, $json){
+        // Clear previous collection data
+        $db->query("PRAGMA foreign_keys = OFF;");
+        $statement = $db->prepare("DELETE FROM collection WHERE uid = :uid;");
+        $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+        $statement->execute();
+        foreach ($json->{"collection"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO collection (uid, unit, open)
+              VALUES (:uid, :unit, :open);
+            ");
+            $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+            $statement->bindValue(":unit", $unit->{"unit_master_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":open", $unit->{"open"}, SQLITE3_INTEGER);
+            $statement->execute();
+        }
+        // Return success
+        return "201 Created.";
+    }
+
+    /**
+     * Parses the response to the GetLobbyWizardLog command and updates the
+     * user records.
+     *
+     * @param resource $db Database connection.
+     * @param int $uid Player UID.
+     * @param mixed[] $json Response.
+     */
+    function parse_records($db, $uid, $json){
+
+        # Page 1: Cairos non-elemental, rift raid, rift dungeon.
+        if (intval($json->{"lobby_wizard_log"}->{"page_no"}) == 1){
+
+            // Update data in player table
+            $db->query("PRAGMA foreign_keys = OFF;");
+            $statement = $db->prepare("
+              UPDATE player SET 
+                joined = :joined,
+                top_rank_arena = :top_rank_arena,
+                top_rank_world_arena = :top_rank_world_arena,
+                top_rank_special_league = :top_rank_special_league,
+                top_rank_gw = :top_rank_gw,
+                top_rank_siege = :top_rank_siege,
+                top_rank_wboss = :top_rank_wboss,
+                top_rank_toan = :top_rank_toan,
+                top_rank_toah = :top_rank_toah
+              WHERE uid = :uid;
+            ");
+            $statement->bindValue(":joined", $json->{"lobby_wizard_log"}->{"account_create_timestamp"}, SQLITE3_TEXT);
+            $statement->bindValue(":top_rank_arena", $json->{"lobby_wizard_log"}->{"pvp_best_rating_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":top_rank_world_arena", $json->{"lobby_wizard_log"}->{"rtpvp_rank_best_rating_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":top_rank_special_league", $json->{"lobby_wizard_log"}->{"rtpvp_contest_best_rating_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":top_rank_gw", $json->{"lobby_wizard_log"}->{"guildwar_best_rating_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":top_rank_siege", $json->{"lobby_wizard_log"}->{"guildsiege_best_rating_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":top_rank_wboss", $json->{"lobby_wizard_log"}->{"world_boss_best_rank_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":top_rank_toan", $json->{"lobby_wizard_log"}->{"trial_tower_normal_best_floor"}, SQLITE3_INTEGER);
+            $statement->bindValue(":top_rank_toah", $json->{"lobby_wizard_log"}->{"trial_tower_hard_best_floor"}, SQLITE3_INTEGER);
+            $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+            $statement->execute();
+
+            // Loop Cairos records
+            foreach ($json->{"lobby_wizard_log"}->{"dungeon_best_clear_info_list"} as $record){
+                // Delete prevous data
+                $statement = $db->prepare("
+                  DELETE FROM record_party
+                  WHERE 
+                    uid = :uid AND
+                    area_type = :area_type AND
+                    area = :area;
+                ");
+                $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::CAIROS_DUNGEON, SQLITE3_INTEGER);
+                $statement->bindValue(":area", $record->{"dungeon_id"}, SQLITE3_INTEGER);
+                $statement->execute();
+                $statement = $db->prepare("
+                  DELETE FROM record
+                  WHERE 
+                    uid = :uid AND
+                    area_type = :area_type AND
+                    area = :area;
+                ");
+                $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::CAIROS_DUNGEON, SQLITE3_INTEGER);
+                $statement->bindValue(":area", $record->{"dungeon_id"}, SQLITE3_INTEGER);
+                $statement->execute();
+
+                // Insert new data
+                $statement = $db->prepare("
+                  INSERT INTO record
+                    (uid, area_type, area, stage, time, score, rank)
+                  VALUES
+                    (:uid, :area_type, :area, :stage, :time, :score, :rank);
+                ");
+                $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::CAIROS_DUNGEON, SQLITE3_INTEGER);
+                $statement->bindValue(":area", $record->{"dungeon_id"}, SQLITE3_INTEGER);
+                $statement->bindValue(":area", $record->{"dungeon_id"}, SQLITE3_INTEGER);
+                $statement->bindValue(":stage", $record->{"stage_id"}, SQLITE3_INTEGER);
+                $statement->bindValue(":time", $record->{"clear_time"}, SQLITE3_INTEGER);
+                $statement->bindValue(":score", 0, SQLITE3_INTEGER);
+                $statement->bindValue(":rank", null, SQLITE3_TEXT);
+                $statement->execute();
+
+                // Loop party
+                foreach ($record->{"my_unit_deck_list"} as $party){
+                    $statement = $db->prepare("
+                      INSERT INTO record_party
+                        (uid, area_type, area, unit, k_unit, leader, front)
+                      VALUES
+                        (:uid, :area_type, :area, :unit, :k_unit, :leader, :front);
+                    ");
+                    $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+                    $statement->bindValue(":area_type", AREA_TYPE_ID::CAIROS_DUNGEON, SQLITE3_INTEGER);
+                    $statement->bindValue(":area", $record->{"dungeon_id"}, SQLITE3_INTEGER);
+                    $statement->bindValue(":unit", $party->{"unit_id"}, SQLITE3_INTEGER);
+                    $statement->bindValue(":k_unit", $party->{"unit_master_id"}, SQLITE3_INTEGER);
+                    $statement->bindValue(":leader", $party->{"leader"}, SQLITE3_INTEGER);
+                    $statement->bindValue(":front", 0, SQLITE3_INTEGER);
+                    $statement->execute();
+                }
+            }
+
+            // Rift Raid record
+            if (sizeof($json->{"lobby_wizard_log"}->{"raid_best_clear_info_list"}) > 0){
+                $record = $json->{"lobby_wizard_log"}->{"raid_best_clear_info_list"}[0];
+
+                // Delete prevous data
+                $statement = $db->prepare("
+                  DELETE FROM record_party 
+                  WHERE 
+                    uid = :uid AND
+                    area_type = :area_type AND
+                    area = :area;
+                ");
+                $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::RIFT_RAID, SQLITE3_INTEGER);
+                $statement->bindValue(":area", $record->{"stage_id"}, SQLITE3_INTEGER);
+                $statement->execute();
+                $statement = $db->prepare("
+                  DELETE FROM record
+                  WHERE 
+                    uid = :uid AND
+                    area_type = :area_type AND
+                    area = :area;
+                ");
+                $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::RIFT_RAID, SQLITE3_INTEGER);
+                $statement->bindValue(":area", $record->{"stage_id"}, SQLITE3_INTEGER);
+                $statement->execute();
+
+                // Insert new data
+                $statement = $db->prepare("
+                  INSERT INTO record
+                    (uid, area_type, area, stage, time, score, rank)
+                  VALUES
+                    (:uid, :area_type, :area, :stage, :time, :score, :rank);
+                ");
+                $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::RIFT_RAID, SQLITE3_INTEGER);
+                $statement->bindValue(":area", $record->{"stage_id"}, SQLITE3_INTEGER);
+                $statement->bindValue(":stage", $record->{"stage_id"}, SQLITE3_INTEGER);
+                $statement->bindValue(":time", $record->{"clear_time"}, SQLITE3_TEXT);
+                $statement->bindValue(":score", 0, SQLITE3_INTEGER);
+                $statement->bindValue(":rank", null, SQLITE3_TEXT);
+                $statement->execute();
+
+                // Loop party
+                foreach ($record->{"my_unit_deck_list"} as $party){
+                    $statement = $db->prepare("
+                      INSERT INTO record_party
+                        (uid, area_type, area, unit, k_unit, leader, front)
+                      VALUES
+                        (:uid, :area_type, :area, :unit, :k_unit, :leader, :front);
+                    ");
+                    $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+                    $statement->bindValue(":area_type", AREA_TYPE_ID::RIFT_RAID, SQLITE3_INTEGER);
+                    $statement->bindValue(":area", $record->{"stage_id"}, SQLITE3_INTEGER);
+                    $statement->bindValue(":unit", $party->{"unit_id"}, SQLITE3_INTEGER);
+                    $statement->bindValue(":k_unit", $party->{"unit_master_id"}, SQLITE3_INTEGER);
+                    $statement->bindValue(":leader", $party->{"leader"}, SQLITE3_INTEGER);
+                    if ($party->{"slot_index"} <= 4){
+                        $statement->bindValue(":front", 1, SQLITE3_INTEGER);
+                    }
+                    else{
+                        $statement->bindValue(":front", 0, SQLITE3_INTEGER);
+                    }
+                    $statement->execute();
+                }
+            }
+
+            // Loop Rift Dungeon records
+            foreach ( $json->{"lobby_wizard_log"}->{"rift_dungeon_best_clear_info_list"} as $record){
+
+                // Delete prevous data
+                $statement = $db->prepare("
+                  DELETE FROM record_party 
+                  WHERE 
+                    uid = :uid AND
+                    area_type = :area_type AND
+                    area = :area;
+                ");
+                $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::RIFT_DUNGEON, SQLITE3_INTEGER);
+                $statement->bindValue(":area", $record->{"rift_dungeon_id"}, SQLITE3_INTEGER);
+                $statement->execute();
+                $statement = $db->prepare("
+                  DELETE FROM record
+                  WHERE 
+                    uid = :uid AND
+                    area_type = :area_type AND
+                    area = :area;
+                ");
+                $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::RIFT_DUNGEON, SQLITE3_INTEGER);
+                $statement->bindValue(":area", $record->{"rift_dungeon_id"}, SQLITE3_INTEGER);
+                $statement->execute();
+
+                // Insert new data
+                $statement = $db->prepare("
+                  INSERT INTO record
+                    (uid, area_type, area, stage, time, score, rank)
+                  VALUES
+                    (:uid, :area_type, :area, :stage, :time, :score, :rank);
+                ");
+                $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::RIFT_DUNGEON, SQLITE3_INTEGER);
+                $statement->bindValue(":area", $record->{"rift_dungeon_id"}, SQLITE3_INTEGER);
+                $statement->bindValue(":stage", 0, SQLITE3_INTEGER);
+                $statement->bindValue(":time", 0, SQLITE3_INTEGER);
+                $statement->bindValue(":score", $record->{"clear_damage"}, SQLITE3_INTEGER);
+                switch (intval($record->{"clear_rating"})){
+                    case 2:
+                        $statement->bindValue(":rank", "D", SQLITE3_TEXT);
+                        break;
+                    case 3:
+                        $statement->bindValue(":rank", "C", SQLITE3_TEXT);
+                        break;
+                    case 4:
+                        $statement->bindValue(":rank", "B-", SQLITE3_TEXT);
+                        break;
+                    case 5:
+                        $statement->bindValue(":rank", "B", SQLITE3_TEXT);
+                        break;
+                    case 6:
+                        $statement->bindValue(":rank", "B+", SQLITE3_TEXT);
+                        break;
+                    case 7:
+                        $statement->bindValue(":rank", "A-", SQLITE3_TEXT);
+                        break;
+                    case 8:
+                        $statement->bindValue(":rank", "A", SQLITE3_TEXT);
+                        break;
+                    case 9:
+                        $statement->bindValue(":rank", "A+", SQLITE3_TEXT);
+                        break;
+                    case 10:
+                        $statement->bindValue(":rank", "S", SQLITE3_TEXT);
+                        break;
+                    case 11:
+                        $statement->bindValue(":rank", "SS", SQLITE3_TEXT);
+                        break;
+                    case 12:
+                        $statement->bindValue(":rank", "SS", SQLITE3_TEXT);
+                        break;
+                    default:
+                        $statement->bindValue(":rank", "F", SQLITE3_TEXT);
+                }
+                $statement->execute();
+
+                // Loop party
+                foreach ($record->{"my_unit_deck_list"} as $party){
+                    $statement = $db->prepare("
+                      INSERT INTO record_party
+                        (uid, area_type, area, unit, k_unit, leader, front)
+                      VALUES
+                        (:uid, :area_type, :area, :unit, :k_unit, :leader, :front);
+                    ");
+                    $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+                    $statement->bindValue(":area_type", AREA_TYPE_ID::RIFT_DUNGEON, SQLITE3_INTEGER);
+                    $statement->bindValue(":area", $record->{"rift_dungeon_id"}, SQLITE3_INTEGER);
+                    $statement->bindValue(":unit", $party->{"unit_id"}, SQLITE3_INTEGER);
+                    $statement->bindValue(":k_unit", $party->{"unit_master_id"}, SQLITE3_INTEGER);
+                    $statement->bindValue(":leader", $party->{"leader"}, SQLITE3_INTEGER);
+                    if ($party->{"slot_index"} <= 4){
+                        $statement->bindValue(":front", 1, SQLITE3_INTEGER);
+                    }
+                    else{
+                        $statement->bindValue(":front", 0, SQLITE3_INTEGER);
+                    }
+                    $statement->execute();
+                }
+            }
+        }
+
+        # Page 2: Cairos non-elemental, rift raid, rift dungeon.
+        elseif (intval($json->{"lobby_wizard_log"}->{"page_no"} == 2)){
+            // Loop Cairos records
+            foreach ($json->{"lobby_wizard_log"}->{"dungeon_best_clear_info_list"} as $record){
+                // Delete prevous data
+                $statement = $db->prepare("
+                  DELETE FROM record_party
+                  WHERE 
+                    uid = :uid AND
+                    area_type = :area_type AND
+                    area = :area;
+                ");
+                $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::CAIROS_DUNGEON, SQLITE3_INTEGER);
+                $statement->bindValue(":area", $record->{"dungeon_id"}, SQLITE3_INTEGER);
+                $statement->execute();
+                $statement = $db->prepare("
+                  DELETE FROM record
+                  WHERE 
+                    uid = :uid AND
+                    area_type = :area_type AND
+                    area = :area;
+                ");
+                $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::CAIROS_DUNGEON, SQLITE3_INTEGER);
+                $statement->bindValue(":area", $record->{"dungeon_id"}, SQLITE3_INTEGER);
+                $statement->execute();
+
+                // Insert new data
+                $statement = $db->prepare("
+                  INSERT INTO record
+                    (uid, area_type, area, stage, time, score, rank)
+                  VALUES
+                    (:uid, :area_type, :area, :stage, :time, :score, :rank);
+                ");
+                $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::CAIROS_DUNGEON, SQLITE3_INTEGER);
+                $statement->bindValue(":area", $record->{"dungeon_id"}, SQLITE3_INTEGER);
+                $statement->bindValue(":area", $record->{"dungeon_id"}, SQLITE3_INTEGER);
+                $statement->bindValue(":stage", $record->{"stage_id"}, SQLITE3_INTEGER);
+                $statement->bindValue(":time", $record->{"clear_time"}, SQLITE3_INTEGER);
+                $statement->bindValue(":score", 0, SQLITE3_INTEGER);
+                $statement->bindValue(":rank", null, SQLITE3_TEXT);
+                $statement->execute();
+
+                // Loop party
+                foreach ($record->{"my_unit_deck_list"} as $party){
+                    $statement = $db->prepare("
+                      INSERT INTO record_party
+                        (uid, area_type, area, unit, k_unit, leader, front)
+                      VALUES
+                        (:uid, :area_type, :area, :unit, :k_unit, :leader, :front);
+                    ");
+                    $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+                    $statement->bindValue(":area_type", AREA_TYPE_ID::CAIROS_DUNGEON, SQLITE3_INTEGER);
+                    $statement->bindValue(":area", $record->{"dungeon_id"}, SQLITE3_INTEGER);
+                    $statement->bindValue(":unit", $party->{"unit_id"}, SQLITE3_INTEGER);
+                    $statement->bindValue(":k_unit", $party->{"unit_master_id"}, SQLITE3_INTEGER);
+                    $statement->bindValue(":leader", $party->{"leader"}, SQLITE3_INTEGER);
+                    $statement->bindValue(":front", 0, SQLITE3_INTEGER);
+                    $statement->execute();
+                }
+            }
+        }
+        elseif (intval($json->{"lobby_wizard_log"}->{"page_no"}) == 3){
+            return "204 Page not logged (only pages 1 and 2 are needed).";
+        }
+        elseif (intval($json->{"lobby_wizard_log"}->{"page_no"}) == 4){
+            return "204 Page not logged (only pages 1 and 2 are needed).";
+        }
+        else{
+            return "400 Unknown page.";
+        }
+
+        // Return success
+        return "201 Created.";
+    }
+
+    try{
+
+        header("Content-type: application/json; charset=utf-8");
+
+        // Get put data
+        $_PUT = [];
+        parse_str(file_get_contents("php://input"), $_PUT);
+
+        // Check API key.
+        $key = $_PUT["key"];
+        if ($key == null || $key == false){
+            header("HTTP/1.1 400 API key not received.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 API key not received.");
+            return 400;
+        }
+
+        // Check data
+        $data = $_PUT["response"];
+        if ($data == null || $data == false){
+            header("HTTP/1.1 400 No data received.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 No data received.");
+            return 400;
+        }
+
+        // Check data format.
+        $json = json_decode($data);
+        if ($json === null){
+            header("HTTP/1.1 400 Data is no valid JSON.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 Data is no valid JSON.");
+            return 400;
+        }
+
+        // Get and validate command
+        $command = $json->{"command"};
+        $accepted_commands = [
+            "HubUserLogin", // Login, full profile
+            "GetUnitCollection", // Unit collection
+            "GetLobbyWizardLog" // Records
+        ];
+        if (!in_array($command, $accepted_commands)){
+            header("HTTP/1.1 405 Command not implemented.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 405 Command not implemented.");
+            return 405;
+        }
+
+        // Get user info and Authenticate
+        switch ($command){
+            case "HubUserLogin":
+                $uid = $json->{"wizard_id"};
+                $statement = $db->prepare("SELECT COUNT(uid) AS c FROM player WHERE uid = :uid AND api_key = :api_key;");
+                $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+                $statement->bindValue(":api_key", $key, SQLITE3_TEXT);
+                if (1 != $statement->execute()->fetchArray(SQLITE3_ASSOC)["c"]){
+                    header("HTTP/1.1 401 Invalid credentials.");
+                    syslog(LOG_INFO, "[APIv3] HTTP/1.1 401 Invalid credentials.");
+                    return 401;
+                }
+                break;
+            case "GetUnitCollection":
+                // No identification in this JSON, get uid from databases
+                $statement = $db->prepare("SELECT uid FROM player WHERE api_key = :api_key;");
+                $statement->bindValue(":api_key", $key, SQLITE3_TEXT);
+                $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
+                if ($r){
+                    $uid = $r["uid"];
+                }
+                else{
+                    header("HTTP/1.1 401 Invalid credentials.");
+                    syslog(LOG_INFO, "[APIv3] HTTP/1.1 401 Invalid credentials.");
+                    return 401;
+                }
+                break;
+            case "GetLobbyWizardLog":
+                $uid = $json->{"lobby_wizard_log"}->{"wizard_id"};
+                $statement = $db->prepare("SELECT COUNT(uid) AS c FROM player WHERE uid = :uid AND api_key = :api_key;");
+                $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+                $statement->bindValue(":api_key", $key, SQLITE3_TEXT);
+                if (1 != $statement->execute()->fetchArray(SQLITE3_ASSOC)["c"]){
+                    header("HTTP/1.1 401 Invalid credentials.");
+                    syslog(LOG_INFO, "[APIv3] HTTP/1.1 401 Invalid credentials.");
+                    return 401;
+                }
+                break;
+        }
+        if ($command == "GetUnitCollection"){ // This command doesnt provide wizard_id
+            $statement = $db->prepare("SELECT COUNT(uid) AS c FROM player WHERE uid = :uid AND api_key = :api_key;");
+            $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+            $statement->bindValue(":api_key", $key, SQLITE3_TEXT);
+            if (1 != $statement->execute()->fetchArray(SQLITE3_ASSOC)["c"]){
+                header("HTTP/1.1 401 Invalid credentials.");
+                syslog(LOG_INFO, "[APIv3] HTTP/1.1 401 Invalid credentials.");
+                return 401;
+            }
+        }
+
+        // Run parser
+        $result = "500 Unknown Error";
+        switch($command){
+            case "HubUserLogin":
+                $result = parse_profile($db, $uid, $json);
+                break;
+            case "GetUnitCollection":
+                $result = parse_collection($db, $uid, $json);
+                break;
+            case "GetLobbyWizardLog":
+                $result = parse_records($db, $uid, $json);
+                break;
+        }
+
+        // Process result
+        $status_code = intval(substr($result, 0, 3));
+        $status_message = substr($result, 4);
+        if ($status_code == 0 || $status_code < 100 || $status_code > 599){
+            header("HTTP/1.1 500 Unknown error.");
+            syslog(LOG_ERR, "[APIv3] HTTP/1.1 500 Unknown error.");
+            return 500;
+        }
+        else{
+            header("HTTP/1.1 $status_code $status_message");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 $status_code $status_message");
+            return $status_code;
+        }
+    }
+    catch(Exception $e) {
+        header("HTTP/1.1 500 Unexpeced error: " . $e->getMessage());
+        syslog(LOG_INFO, "[APIv3] HTTP/1.1 500 Unexpected error: " . $e->getMessage());
+        return 500;
+    }
+?>

+ 1469 - 0
application/API/v3/run/POST.php

@@ -0,0 +1,1469 @@
+<?php
+    /**
+     * Run logger script.
+     *
+     * Exposes an API to log a run.
+     *
+     * Mandatory PUT parameters are:
+     *  - result_response: JSON received from game server on run end.
+     *  - key: User API key.
+     *
+     * Optional POST parameters are:
+     *  - result_request: JSON sent to game server on run end.
+     *  - start_response: JSON received from game server on run start.
+     *  - start_request: JSON sent to game server on run start.
+     *
+     * @category API
+     */
+
+    /**
+     * Calculates the currrent efficiency of a rune.
+     *
+     * @param mixed[] $rune JSON fragment of the rune.
+     * @return int Efficiency (0-100)
+     */
+    function calculate_efficiency($rune){
+        $efficiency = 0.0;
+        $level = $rune->{"upgrade_curr"};
+        $stars = $rune->{"class"};
+        // Ancient stars are + 10
+        if ($stars > 10){
+            $stars -= 10;
+        }
+        $list = null;
+        // Main stat
+        $main_stat = $rune->{"pri_eff"}[0];
+        switch ($main_stat){
+            case RUNE_STAT_ID::HP:
+                $list = RUNE_STAT_VALUES::HP;
+                break;
+            case RUNE_STAT_ID::HP_P:
+                $list = RUNE_STAT_VALUES::HP_P;
+                break;
+            case RUNE_STAT_ID::ATK:
+                $list = RUNE_STAT_VALUES::ATK;
+                break;
+            case RUNE_STAT_ID::ATK_P:
+                $list = RUNE_STAT_VALUES::ATK_P;
+                break;
+            case RUNE_STAT_ID::DEF:
+                $list = RUNE_STAT_VALUES::DEF;
+                break;
+            case RUNE_STAT_ID::DEF_P:
+                $list = RUNE_STAT_VALUES::DEF_P;
+                break;
+            case RUNE_STAT_ID::SPD:
+                $list = RUNE_STAT_VALUES::SPD;
+                break;
+            case RUNE_STAT_ID::CRR:
+                $list = RUNE_STAT_VALUES::CRR;
+                break;
+            case RUNE_STAT_ID::CRD:
+                $list = RUNE_STAT_VALUES::CRD;
+                break;
+            case RUNE_STAT_ID::ACC:
+                $list = RUNE_STAT_VALUES::ACC;
+                break;
+            case RUNE_STAT_ID::RES:
+                $list = RUNE_STAT_VALUES::RES;
+                break;
+        }
+        $efficiency += floatval($list[$stars][$level]) / floatval($list[6][15]);
+        // Innate stat
+        if ($rune->{"prefix_eff"}[0] > 0){
+            $innate_stat = $rune->{"pri_eff"}[0];
+            $innate_stat_value = $rune->{"pri_eff"}[1];
+            $efficiency += floatval($innate_stat_value) / (5 * floatval(RUNE_SUBSTAT_MAX_VALUES::SUBSTAT[$innate_stat][6]));
+        }
+        // Substats
+        for ($i = 0; $i <= 3; $i ++){
+            if (sizeof($rune->{"sec_eff"}) > $i){
+                $substat = $rune->{"sec_eff"}[$i][0];
+                $substat_value = $rune->{"sec_eff"}[$i][1];
+                $efficiency += floatval($substat_value) / (5 * floatval(RUNE_SUBSTAT_MAX_VALUES::SUBSTAT[$substat][6]));
+            }
+        }
+        $efficiency = $efficiency / 2.8 * 100;
+        if ($efficiency > 100){
+            $efficiency = 100;
+        }
+        return $efficiency;
+    }
+
+    /**
+     * Calculates the maximum efficiency of a rune.
+     *
+     * @param mixed[] $rune JSON fragment of the rune.
+     * @return int Maximum efficiency (0-100)
+     */
+    function calculate_maximum_efficiency($rune){
+        $efficiency = 0.0;
+        $stars = $rune->{"class"};
+        // Ancient stars are + 10
+        if ($stars > 10){
+            $stars -= 10;
+        }
+        $list = null;
+        // Main stat
+        $main_stat = $rune->{"pri_eff"}[0];
+        switch ($main_stat){
+            case RUNE_STAT_ID::HP:
+                $list = RUNE_STAT_VALUES::HP;
+                break;
+            case RUNE_STAT_ID::HP_P:
+                $list = RUNE_STAT_VALUES::HP_P;
+                break;
+            case RUNE_STAT_ID::ATK:
+                $list = RUNE_STAT_VALUES::ATK;
+                break;
+            case RUNE_STAT_ID::ATK_P:
+                $list = RUNE_STAT_VALUES::ATK_P;
+                break;
+            case RUNE_STAT_ID::DEF:
+                $list = RUNE_STAT_VALUES::DEF;
+                break;
+            case RUNE_STAT_ID::DEF_P:
+                $list = RUNE_STAT_VALUES::DEF_P;
+                break;
+            case RUNE_STAT_ID::SPD:
+                $list = RUNE_STAT_VALUES::SPD;
+                break;
+            case RUNE_STAT_ID::CRR:
+                $list = RUNE_STAT_VALUES::CRR;
+                break;
+            case RUNE_STAT_ID::CRD:
+                $list = RUNE_STAT_VALUES::CRD;
+                break;
+            case RUNE_STAT_ID::ACC:
+                $list = RUNE_STAT_VALUES::ACC;
+                break;
+            case RUNE_STAT_ID::RES:
+                $list = RUNE_STAT_VALUES::RES;
+                break;
+        }
+        $efficiency += floatval($list[$stars][15]) / floatval($list[6][15]);
+        // Innate stat
+        if ($rune->{"prefix_eff"}[0] > 0){
+            $innate_stat = $rune->{"pri_eff"}[0];
+            $innate_stat_value = $rune->{"pri_eff"}[1];
+            $efficiency += floatval($innate_stat_value) / (5 * floatval(RUNE_SUBSTAT_MAX_VALUES::SUBSTAT[$innate_stat][6]));
+        }
+        // Substats
+        for ($i = 0; $i <= 3; $i ++){
+            if (sizeof($rune->{"sec_eff"}) > $i){
+                $substat = $rune->{"sec_eff"}[$i][0];
+                $efficiency += floatval(RUNE_SUBSTAT_MAX_VALUES::SUBSTAT[$substat][$stars]) / (5 * floatval(RUNE_SUBSTAT_MAX_VALUES::SUBSTAT[$substat][6]));
+            }
+        }
+        $efficiency = $efficiency / 2.8 * 100;
+        if ($efficiency > 100){
+            $efficiency = 100;
+        }
+        return $efficiency;
+    }
+
+    /**
+     * Parses the JSONs relateds to an Arena run and saves the data.
+     *
+     * @param resource $db Database connection.
+     * @param int $uid Player UID.
+     * @param int $id Run ID.
+     * @param mixed[] $json List of received JSONs.
+     */
+    function log_arena_run($db, $id, $uid, $json){
+        // Only the result request and response JSONs are needed:
+        $response = $json["result_res"];
+
+        // Prepare the "run" insert.
+        $statement = $db->prepare("
+          INSERT INTO run (
+            uid,
+            id,
+            dtime,
+            area_type,
+            area,
+            stage,
+            difficulty,
+            win,
+            time,
+            mana,
+            energy,
+            crystal,
+            guild_points,
+            honor_points,
+            helper,
+            score,
+            rank
+          ) VALUES (
+            :uid,
+            :id,
+            :dtime,
+            :area_type,
+            :area,
+            :stage,
+            :difficulty,
+            :win,
+            :time,
+            :mana,
+            :energy,
+            :crystal,
+            :guild_points,
+            :honor_points,
+            :helper,
+            :score,
+            :rank
+          );
+        ");
+
+        // Bind data and insert
+        $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+        $statement->bindValue(":id", $id, SQLITE3_INTEGER);
+        $statement->bindValue(":dtime", $response->{"tvaluelocal"}, SQLITE3_TEXT);
+        $statement->bindValue(":area_type", AREA_TYPE_ID::ARENA, SQLITE3_INTEGER);
+        $statement->bindValue(":area", AREA_TYPE_ID::ARENA, SQLITE3_INTEGER);
+        $statement->bindValue(":stage", null, SQLITE3_INTEGER);
+        $statement->bindValue(":difficulty", null, SQLITE3_INTEGER);
+        $statement->bindValue(":win", $response->{"win_lose"}, SQLITE3_INTEGER);
+        $statement->bindValue(":time", 1, SQLITE3_INTEGER);
+        if (array_key_exists("mana", $response->{"reward"})){
+            $statement->bindValue(":mana", $response->{"reward"}->{"mana"}, SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":mana", 0, SQLITE3_INTEGER);
+        }
+        if (array_key_exists("energy", $response->{"reward"})){
+            $statement->bindValue(":energy", $response->{"reward"}->{"energy"}, SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":energy", 0, SQLITE3_INTEGER);
+        }
+        if (array_key_exists("crystal", $response->{"reward"})){
+            $statement->bindValue(":crystal", $response->{"reward"}->{"crystal"}, SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":crystal", 0, SQLITE3_INTEGER);
+        }
+        $statement->bindValue(":guild_points", 0, SQLITE3_INTEGER);
+        if (array_key_exists("honor_point", $response->{"reward"})){
+            $statement->bindValue(":honor_points", $response->{"reward"}->{"honor_point"}, SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":honor_points", 0, SQLITE3_INTEGER);
+        }
+        $statement->bindValue(":helper", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":score", null, SQLITE3_INTEGER);
+        $statement->bindValue(":rank", null, SQLITE3_TEXT);
+        $statement->execute();
+
+        // Parse party
+        $leader = 1;
+        foreach ($response->{"unit_list"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO run_party 
+                (run, unit, k_unit, leader, front)
+              VALUES
+                (:run, :unit, :k_unit, :leader, :front);"
+            );
+            $statement->bindValue(":run", $id, SQLITE3_INTEGER);
+            $statement->bindValue(":unit", $unit->{"unit_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":k_unit", $unit->{"unit_master_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":leader", $leader, SQLITE3_INTEGER);
+            $leader = 0;
+            $statement->bindValue(":front", 0, SQLITE3_INTEGER);
+            $statement->execute();
+        }
+
+        // Return success
+        return "201 Created.";
+    }
+
+    /**
+     * Parses the JSONs relateds to a Dimensional Rift run and saves the data.
+     *
+     * @param resource $db Database connection.
+     * @param int $uid Player UID.
+     * @param int $id Run ID.
+     * @param mixed[] $json List of received JSONs.
+     */
+    function log_dark_portal_run($db, $id, $uid, $json){
+        // Only the result request and response JSONs are needed:
+        $response = $json["result_res"];
+
+        // Prepare the "run" insert.
+        $statement = $db->prepare("
+          INSERT INTO run (
+            uid,
+            id,
+            dtime,
+            area_type,
+            area,
+            stage,
+            difficulty,
+            win,
+            time,
+            mana,
+            energy,
+            crystal,
+            guild_points,
+            honor_points,
+            helper,
+            score,
+            rank
+          ) VALUES (
+            :uid,
+            :id,
+            :dtime,
+            :area_type,
+            :area,
+            :stage,
+            :difficulty,
+            :win,
+            :time,
+            :mana,
+            :energy,
+            :crystal,
+            :guild_points,
+            :honor_points,
+            :helper,
+            :score,
+            :rank
+          );
+        ");
+
+        // Bind data and insert
+        $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+        $statement->bindValue(":id", $id, SQLITE3_INTEGER);
+        $statement->bindValue(":dtime", $response->{"tvaluelocal"}, SQLITE3_INTEGER);
+        $statement->bindValue(":area_type", AREA_TYPE_ID::DIMENSIONAL_RIFT, SQLITE3_INTEGER);
+        // The area is the region the portal is open in.
+        $statement->bindValue(":area", $response->{"region_id"}, SQLITE3_INTEGER);
+        $statement->bindValue(":stage", null, SQLITE3_INTEGER);
+        $statement->bindValue(":difficulty", $response->{"difficulty"}, SQLITE3_INTEGER);
+        $statement->bindValue(":win", $response->{"win_lose"}, SQLITE3_INTEGER);
+        $statement->bindValue(":time", 1, SQLITE3_INTEGER);
+        if (array_key_exists("mana", $response->{"reward"})){
+            $statement->bindValue(":mana", $response->{"reward"}->{"mana"}, SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":mana", 0, SQLITE3_INTEGER);
+        }
+        if (array_key_exists("energy", $response->{"reward"})){
+            $statement->bindValue(":energy", $response->{"reward"}->{"energy"});
+        }
+        else{
+            $statement->bindValue(":energy", 0);
+        }
+        if (array_key_exists("crystal", $response->{"reward"})){
+            $statement->bindValue(":crystal", $response->{"reward"}->{"crystal"}, SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":crystal", 0, SQLITE3_INTEGER);
+        }
+        $statement->bindValue(":guild_points", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":honor_points", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":helper", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":score", null, SQLITE3_INTEGER);
+        $statement->bindValue(":rank", null, SQLITE3_TEXT);
+        $statement->execute();
+
+        // Parse party
+        $leader = 1;
+        foreach ($response->{"unit_list"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO run_party 
+                (run, unit, k_unit, leader, front)
+              VALUES
+                (:run, :unit, :k_unit, :leader, :front);"
+            );
+            $statement->bindValue(":run", $id, SQLITE3_INTEGER);
+            $statement->bindValue(":unit", $unit->{"unit_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":k_unit", $unit->{"unit_master_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":leader", $leader, SQLITE3_INTEGER);
+            $leader = 0;
+            $statement->bindValue(":front", 0, SQLITE3_INTEGER);
+            $statement->execute();
+        }
+
+        // Return success
+        return "201 Created.";
+    }
+
+    /**
+     * Parses the JSONs relateds to a scenario run and saves the data.
+     *
+     * @param resource $db Database connection.
+     * @param int $uid Player UID.
+     * @param int $id Run ID.
+     * @param mixed[] $json List of received JSONs.
+     */
+    function log_scenario_run($db, $id, $uid, $json){
+        // Only the result request and response JSONs are needed:
+        $response = $json["result_res"];
+
+        if ($json["start_res"] === null){
+            return ("400 Start response is required to parse scenario runs.");
+        }
+        $request = $json["start_res"];
+
+        // Prepare the "run" insert.
+        $statement = $db->prepare("
+          INSERT INTO run (
+            uid,
+            id,
+            dtime,
+            area_type,
+            area,
+            stage,
+            difficulty,
+            win,
+            time,
+            mana,
+            energy,
+            crystal,
+            guild_points,
+            honor_points,
+            helper,
+            score,
+            rank
+          ) VALUES (
+            :uid,
+            :id,
+            :dtime,
+            :area_type,
+            :area,
+            :stage,
+            :difficulty,
+            :win,
+            :time,
+            :mana,
+            :energy,
+            :crystal,
+            :guild_points,
+            :honor_points,
+            :helper,
+            :score,
+            :rank
+          );
+        ");
+
+        // Bind data and insert
+        $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+        $statement->bindValue(":id", $id, SQLITE3_INTEGER);
+        $statement->bindValue(":dtime", $response->{"tvaluelocal"}, SQLITE3_TEXT);
+        $statement->bindValue(":area_type", AREA_TYPE_ID::SCENARIO, SQLITE3_INTEGER);
+        $statement->bindValue(":area", $response->{"scenario_info"}->{"region_id"}, SQLITE3_INTEGER);
+        $statement->bindValue(":stage", $response->{"scenario_info"}->{"stage_no"}, SQLITE3_INTEGER);
+        $statement->bindValue(":difficulty", $response->{"scenario_info"}->{"difficulty"}, SQLITE3_INTEGER);
+        $statement->bindValue(":win", $response->{"win_lose"}, SQLITE3_INTEGER);
+        $statement->bindValue(":time", $response->{"clear_time"}->{"current_time"}, SQLITE3_INTEGER);
+        if (array_key_exists("mana", $response->{"reward"})){
+            $statement->bindValue(":mana", $response->{"reward"}->{"mana"}, SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":mana", 0, SQLITE3_INTEGER);
+        }
+        if (array_key_exists("energy", $response->{"reward"})){
+            $statement->bindValue(":energy", $response->{"reward"}->{"energy"}, SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":energy", 0, SQLITE3_INTEGER);
+        }
+        if (array_key_exists("crystal", $response->{"reward"})){
+            $statement->bindValue(":crystal", $response->{"reward"}->{"crystal"}, SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":crystal", 0, SQLITE3_INTEGER);
+        }
+        $statement->bindValue(":guild_points", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":honor_points", 0, SQLITE3_INTEGER);
+        if (array_key_exists("helper_unit_list", $request) && sizeof($request->{"helper_unit_list"}) > 0){
+            $statement->bindValue(":helper", 1, SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":helper", 0, SQLITE3_INTEGER);
+        }
+        $statement->bindValue(":score", null, SQLITE3_INTEGER);
+        $statement->bindValue(":rank", null, SQLITE3_TEXT);
+        $statement->execute();
+
+        // Parse various types of reward
+        if (array_key_exists("crate", $response->{"reward"})){
+            $crate = $response->{"reward"}->{"crate"};
+            if (array_key_exists("unit_info", $crate)){
+                $unit = $crate->{"unit_info"};
+                $statement = $db->prepare("INSERT INTO run_drop_unit (run, unit) VALUES (:run, :unit);");
+                $statement->bindValue(":run", $id, SQLITE3_INTEGER);
+                $statement->bindValue(":unit", $unit->{"unit_master_id"}, SQLITE3_INTEGER);
+                $statement->execute();
+            }
+            if (array_key_exists("craft_stuff", $crate)){
+                $item = $crate->{"craft_stuff"};
+                $statement = $db->prepare("INSERT INTO run_drop_item (run, item, amount) VALUES (:run, :item, :amount);");
+                $statement->bindValue(":run", $id, SQLITE3_INTEGER);
+                $statement->bindValue(":item", $item->{"item_master_id"}, SQLITE3_INTEGER);
+                $statement->bindValue(":amount", $item->{"item_quantity"}, SQLITE3_INTEGER);
+                $statement->execute();
+            }
+            if (array_key_exists("random_scroll", $crate)){
+                $item = $crate->{"random_scroll"};
+                $statement = $db->prepare("INSERT INTO run_drop_item (run, item, amount) VALUES (:run, :item, :amount);");
+                $statement->bindValue(":run", $id, SQLITE3_INTEGER);
+                $statement->bindValue(":item", $item->{"item_master_id"}, SQLITE3_INTEGER);
+                $statement->bindValue(":amount", $item->{"item_quantity"}, SQLITE3_INTEGER);
+                $statement->execute();
+            }
+            if (array_key_exists("rune", $crate)){
+                $rune = $crate->{"rune"};
+                $statement = $db->prepare("
+                    INSERT INTO run_drop_rune (
+                    run,
+                    id,
+                    type,
+                    slot,
+                    stars,
+                    ancient,
+                    quality,
+                    value,
+                    efficiency,
+                    max_efficiency,
+                    main_stat,
+                    main_stat_value,
+                    innate_stat,
+                    innate_stat_value,
+                    substat_1,
+                    substat_1_value,
+                    substat_2,
+                    substat_2_value,
+                    substat_3,
+                    substat_3_value,
+                    substat_4,
+                    substat_4_value
+                    ) VALUES (
+                    :run,
+                    :id,
+                    :type,
+                    :slot,
+                    :stars,
+                    :ancient,
+                    :quality,
+                    :value,
+                    :efficiency,
+                    :max_efficiency,
+                    :main_stat,
+                    :main_stat_value,
+                    :innate_stat,
+                    :innate_stat_value,
+                    :substat_1,
+                    :substat_1_value,
+                    :substat_2,
+                    :substat_2_value,
+                    :substat_3,
+                    :substat_3_value,
+                    :substat_4,
+                    :substat_4_value
+                    );
+                ");
+                $statement->bindValue(":run", $id);
+                $statement->bindValue(":id", $rune->{"rune_id"});
+                $statement->bindValue(":type", $rune->{"set_id"});
+                $statement->bindValue(":slot", $rune->{"slot_no"});
+                $statement->bindValue(":value", $rune->{"sell_value"});
+                if ($rune->{"class"} > 10){
+                    // Ancient rune
+                    $statement->bindValue(":ancient", 1, SQLITE3_INTEGER);
+                    $statement->bindValue(":stars", intval($rune->{"class"}) - 10, SQLITE3_INTEGER);
+                    $statement->bindValue(":quality", intval($rune->{"rank"}) - 10, SQLITE3_INTEGER);
+                }
+                else{
+                    // Non Ancient rune
+                    $statement->bindValue(":ancient", 0, SQLITE3_INTEGER);
+                    $statement->bindValue(":stars", $rune->{"class"}, SQLITE3_INTEGER);
+                    $statement->bindValue(":quality", $rune->{"rank"}, SQLITE3_INTEGER);
+                }
+                $efficiency = calculate_efficiency($rune);
+                $max_efficiency = calculate_maximum_efficiency($rune);
+                $statement->bindValue(":efficiency", $efficiency, SQLITE3_FLOAT);
+                $statement->bindValue(":max_efficiency", $max_efficiency, SQLITE3_FLOAT);
+                $statement->bindValue(":main_stat", $rune->{"pri_eff"}[0], SQLITE3_INTEGER);
+                $statement->bindValue(":main_stat_value", $rune->{"pri_eff"}[1], SQLITE3_INTEGER);
+                if ($rune->{"prefix_eff"}[0] > 0){
+                    $statement->bindValue(":innate_stat", $rune->{"prefix_eff"}[0], SQLITE3_INTEGER);
+                    $statement->bindValue(":innate_stat_value", $rune->{"prefix_eff"}[1], SQLITE3_INTEGER);
+                }
+                else{
+                    $statement->bindValue(":innate_stat", null, SQLITE3_INTEGER);
+                    $statement->bindValue(":innate_stat_value", 0, SQLITE3_INTEGER);
+                }
+                if (sizeof($rune->{"sec_eff"}) > 0){
+                    $statement->bindValue(":substat_1", $rune->{"sec_eff"}[0][0], SQLITE3_INTEGER);
+                    $statement->bindValue(":substat_1_value", $rune->{"sec_eff"}[0][1], SQLITE3_INTEGER);
+                }
+                else{
+                    $statement->bindValue(":substat_1", null, SQLITE3_INTEGER);
+                    $statement->bindValue(":substat_1_value", 0, SQLITE3_INTEGER);
+                }
+                if (sizeof($rune->{"sec_eff"}) > 1){
+                    $statement->bindValue(":substat_2", $rune->{"sec_eff"}[1][0], SQLITE3_INTEGER);
+                    $statement->bindValue(":substat_2_value", $rune->{"sec_eff"}[1][1], SQLITE3_INTEGER);
+                }
+                else{
+                    $statement->bindValue(":substat_2", null, SQLITE3_INTEGER);
+                    $statement->bindValue(":substat_2_value", 0, SQLITE3_INTEGER);
+                }
+                if (sizeof($rune->{"sec_eff"}) > 2){
+                    $statement->bindValue(":substat_3", $rune->{"sec_eff"}[2][0], SQLITE3_INTEGER);
+                    $statement->bindValue(":substat_3_value", $rune->{"sec_eff"}[2][1], SQLITE3_INTEGER);
+                }
+                else{
+                    $statement->bindValue(":substat_3", null, SQLITE3_INTEGER);
+                    $statement->bindValue(":substat_3_value", 0, SQLITE3_INTEGER);
+                }
+                if (sizeof($rune->{"sec_eff"}) > 3){
+                    $statement->bindValue(":substat_4", $rune->{"sec_eff"}[3][0], SQLITE3_INTEGER);
+                    $statement->bindValue(":substat_4_value", $rune->{"sec_eff"}[3][1], SQLITE3_INTEGER);
+                }
+                else{
+                    $statement->bindValue(":substat_4", null, SQLITE3_INTEGER);
+                    $statement->bindValue(":substat_4_value", 0, SQLITE3_INTEGER);
+                }
+                $statement->execute();
+            }
+        }
+
+        // Parse party
+        $leader = 1;
+        foreach ($response->{"unit_list"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO run_party 
+                (run, unit, k_unit, leader, front)
+              VALUES
+                (:run, :unit, :k_unit, :leader, :front);"
+            );
+            $statement->bindValue(":run", $id, SQLITE3_INTEGER);
+            $statement->bindValue(":unit", $unit->{"unit_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":k_unit", $unit->{"unit_master_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":leader", $leader, SQLITE3_INTEGER);
+            $leader = 0;
+            $statement->bindValue(":front", 0, SQLITE3_INTEGER);
+            $statement->execute();
+        }
+
+        // Return success
+        return "201 Created.";
+    }
+
+    /**
+     * Parses the JSONs relateds to a cairos run and saves the data.
+     *
+     * @param resource $db Database connection.
+     * @param int $uid Player UID.
+     * @param int $id Run ID.
+     * @param mixed[] $json List of received JSONs.
+     */
+    function log_rift_raid_run($db, $id, $uid, $json){
+        // Only the start and result requests JSONs are needed:
+        $result = $json["result_res"];
+        if ($json["start_res"] === null){
+            return ("400 Start response is required to parse Rift Raid runs.");
+        }
+        $start = $json["start_res"];
+
+        // Prepare the "run" insert.
+        $statement = $db->prepare("
+          INSERT INTO run (
+            uid,
+            id,
+            dtime,
+            area_type,
+            area,
+            stage,
+            difficulty,
+            win,
+            time,
+            mana,
+            energy,
+            crystal,
+            guild_points,
+            honor_points,
+            helper,
+            score,
+            rank
+          ) VALUES (
+            :uid,
+            :id,
+            :dtime,
+            :area_type,
+            :area,
+            :stage,
+            :difficulty,
+            :win,
+            :time,
+            :mana,
+            :energy,
+            :crystal,
+            :guild_points,
+            :honor_points,
+            :helper,
+            :score,
+            :rank
+          );
+        ");
+
+        // Bind data and insert
+        $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+        $statement->bindValue(":id", $id, SQLITE3_INTEGER);
+        $statement->bindValue(":dtime", $result->{"tvaluelocal"}, SQLITE3_TEXT);
+        $statement->bindValue(":area_type", AREA_TYPE_ID::RIFT_RAID, SQLITE3_INTEGER);
+        $statement->bindValue(":area", AREA_TYPE_ID::RIFT_RAID, SQLITE3_INTEGER);
+        $statement->bindValue(":stage", $start->{"battle_info"}->{"stage_id"}, SQLITE3_INTEGER);
+        $statement->bindValue(":difficulty", null, SQLITE3_INTEGER);
+        $statement->bindValue(":win", $result->{"win_lose"}, SQLITE3_INTEGER);
+        if ($result->{"win_lose"} == 1){
+            $statement->bindValue(":time", $result->{"clear_time"}->{"current_time"}, SQLITE3_TEXT);
+        }
+        else{
+            $statement->bindValue(":time", 1, SQLITE3_INTEGER);
+        }
+        $statement->bindValue(":mana", 0, SQLITE3_INTEGER); // If reward is mana, i will be updated later
+        $statement->bindValue(":energy", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":crystal", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":guild_points", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":honor_points", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":helper", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":score", null, SQLITE3_INTEGER);
+        $statement->bindValue(":rank", null, SQLITE3_TEXT);
+        $statement->execute();
+
+        // Parse various types of reward
+        if ($result->{"win_lose"} == 1 && array_key_exists("battle_reward_list", $result)){
+            foreach ($result->{"battle_reward_list"} as $reward_list){
+                if ($reward_list->{"wizard_id"} == $uid){
+                    $reward = $reward_list->{"reward_list"}[0];
+                    $reward_type = $reward->{"item_master_type"};
+                    switch ($reward_type){
+                        case INVENTORY_TYPE_ID::MONSTER: // Unit
+                            $statement = $db->prepare("INSERT INTO run_drop_unit (run, unit) VALUES (:run, :unit);");
+                            $statement->bindValue(":run", $id, SQLITE3_INTEGER);
+                            $statement->bindValue(":unit", $reward->{"item_master_id"}, SQLITE3_INTEGER);
+                            $statement->execute();
+                            break;
+                        case INVENTORY_TYPE_ID::SCROLL:
+                        case INVENTORY_TYPE_ID::ESSENCES:
+                            $statement = $db->prepare("INSERT INTO run_drop_item (run, item, amount) VALUES (:run, :item, :amount);");
+                            $statement->bindValue(":run", $id, SQLITE3_INTEGER);
+                            $statement->bindValue(":item", $reward->{"item_master_id"}, SQLITE3_INTEGER);
+                            $statement->bindValue(":amount", $reward->{"item_quantity"}, SQLITE3_INTEGER);
+                            $statement->execute();
+                            break;
+                        case INVENTORY_TYPE_ID::ENCHANTMENT:
+                            $statement = $db->prepare("
+                              INSERT INTO run_drop_enchantment (run, id, type, quality, rune, stat, value) 
+                              VALUES (:run, :id, :type, :quality, :rune, :stat, :value);
+                            ");
+                            $statement->bindValue(":run", $id, SQLITE3_INTEGER);
+                            $statement->bindValue(":id", $reward->{"runecraft_item_id"}, SQLITE3_INTEGER);
+                            $statement->bindValue(":type", $reward->{"runecraft_type"}, SQLITE3_INTEGER);
+                            $statement->bindValue(":quality", $reward->{"runecraft_rank"}, SQLITE3_INTEGER);
+                            $statement->bindValue(":rune", $reward->{"runecraft_set_id"}, SQLITE3_INTEGER);
+                            $statement->bindValue(":stat", $reward->{"runecraft_effect_id"}, SQLITE3_INTEGER);
+                            $statement->bindValue(":value", 0, SQLITE3_INTEGER);
+                            $statement->execute();
+                            break;
+                        case 102: //: Mana
+                            $statement = $db->prepare("UPDATE run SET mana = :mana WHERE run = :run;");
+                            $statement->bindValue(":run", $id, SQLITE3_INTEGER);
+                            $statement->bindValue(":id", $reward->{"runecraft_item_id"}, SQLITE3_INTEGER);
+                            $statement->execute();
+                            break;
+                    }
+                }
+            }
+        }
+
+        // Parse party
+        foreach ($start->{"battle_info"}->{"user_list"} as $user){
+            if ($user->{"wizard_id"} == $uid){
+                foreach ($user->{"deck_list"} as $unit){
+                    $statement = $db->prepare("
+                      INSERT INTO run_party 
+                        (run, unit, k_unit, leader, front)
+                      VALUES
+                        (:run, :unit, :k_unit, :leader, :front);"
+                    );
+                    $statement->bindValue(":run", $id, SQLITE3_INTEGER);
+                    $statement->bindValue(":unit", $unit->{"unit_info"}->{"unit_id"}, SQLITE3_INTEGER);
+                    $statement->bindValue(":k_unit", $unit->{"unit_info"}->{"unit_master_id"}, SQLITE3_INTEGER);
+                    $statement->bindValue(":leader", $unit->{"leader"}, SQLITE3_INTEGER);
+                    if (intval($unit->{"index"}) <= 4){
+                        $statement->bindValue(":front", 1, SQLITE3_INTEGER);
+                    }
+                    else{
+                        $statement->bindValue(":front", 0, SQLITE3_INTEGER);
+                    }
+                    $statement->execute();
+                }
+            }
+        }
+
+        // Return success
+        return "201 Created.";
+    }
+
+    /**
+     * Parses the JSONs relateds to a cairos run and saves the data.
+     *
+     * @param resource $db Database connection.
+     * @param int $uid Player UID.
+     * @param int $id Run ID.
+     * @param mixed[] $json List of received JSONs.
+     */
+    function log_cairos_run($db, $id, $uid, $json){
+        // Only the result request and response JSONs are needed:
+        $response = $json["result_res"];
+        if ($json["result_req"] === null){
+            return ("400 Result request is required to parse Cairos runs.");
+        }
+        $request = $json["result_req"];
+
+        // Prepare the "run" insert.
+        $statement = $db->prepare("
+          INSERT INTO run (
+            uid,
+            id,
+            dtime,
+            area_type,
+            area,
+            stage,
+            difficulty,
+            win,
+            time,
+            mana,
+            energy,
+            crystal,
+            guild_points,
+            honor_points,
+            helper,
+            score,
+            rank
+          ) VALUES (
+            :uid,
+            :id,
+            :dtime,
+            :area_type,
+            :area,
+            :stage,
+            :difficulty,
+            :win,
+            :time,
+            :mana,
+            :energy,
+            :crystal,
+            :guild_points,
+            :honor_points,
+            :helper,
+            :score,
+            :rank
+          );
+        ");
+
+        // Bind data and insert
+        $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+        $statement->bindValue(":id", $id, SQLITE3_INTEGER);
+        $statement->bindValue(":dtime", $response->{"tvaluelocal"}, SQLITE3_TEXT);
+        $statement->bindValue(":area_type", AREA_TYPE_ID::CAIROS_DUNGEON, SQLITE3_INTEGER);
+        $statement->bindValue(":area", $request->{"dungeon_id"}, SQLITE3_INTEGER);
+        $statement->bindValue(":stage", $request->{"stage_id"}, SQLITE3_INTEGER);
+        $statement->bindValue(":difficulty", null, SQLITE3_INTEGER);
+        $statement->bindValue(":win", $response->{"win_lose"}, SQLITE3_INTEGER);
+        $statement->bindValue(":time", $response->{"clear_time"}->{"current_time"}, SQLITE3_INTEGER);
+        if (array_key_exists("mana", $response->{"reward"})){
+            $statement->bindValue(":mana", $response->{"reward"}->{"mana"}, SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":mana", 0, SQLITE3_INTEGER);
+        }
+        if (array_key_exists("energy", $response->{"reward"})){
+            $statement->bindValue(":energy", $response->{"reward"}->{"energy"}, SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":energy", 0, SQLITE3_INTEGER);
+        }
+        if (array_key_exists("crystal", $response->{"reward"})){
+            $statement->bindValue(":crystal", $response->{"reward"}->{"crystal"}, SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":crystal", 0, SQLITE3_INTEGER);
+        }
+        $statement->bindValue(":guild_points", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":honor_points", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":helper", 0, SQLITE3_INTEGER); // TODO Where is this information??
+        $statement->bindValue(":score", null, SQLITE3_INTEGER);
+        $statement->bindValue(":rank", null, SQLITE3_TEXT);
+        $statement->execute();
+
+        // Parse various types of reward
+        if (array_key_exists("changed_item_list", $response)){
+            $reward_type = $response->{"changed_item_list"}[0]->{"type"};
+            switch ($reward_type){
+                case INVENTORY_TYPE_ID::MONSTER: // Unit
+                    $statement = $db->prepare("INSERT INTO run_drop_unit (run, unit) VALUES (:run, :unit);");
+                    $statement->bindValue(":run", $id, SQLITE3_INTEGER);
+                    $statement->bindValue(":unit", $response->{"changed_item_list"}[0]->{"view"}->{"item_master_id"}, SQLITE3_INTEGER);
+                    $statement->execute();
+                    break;
+                case INVENTORY_TYPE_ID::SCROLL:
+                case INVENTORY_TYPE_ID::ESSENCES:
+                    $statement = $db->prepare("INSERT INTO run_drop_item (run, item, amount) VALUES (:run, :item, :amount);");
+                    $statement->bindValue(":run", $id, SQLITE3_INTEGER);
+                    $statement->bindValue(":item", $response->{"changed_item_list"}[0]->{"view"}->{"item_master_id"}, SQLITE3_INTEGER);
+                    $statement->bindValue(":amount", $response->{"changed_item_list"}[0]->{"view"}->{"item_quantity"}, SQLITE3_INTEGER);
+                    $statement->execute();
+                    break;
+                case INVENTORY_TYPE_ID::RUNE:
+                    $rune = $response->{"changed_item_list"}[0]->{"info"};
+                    $statement = $db->prepare("
+                      INSERT INTO run_drop_rune (
+                        run,
+                        id,
+                        type,
+                        slot,
+                        stars,
+                        ancient,
+                        quality,
+                        value,
+                        efficiency,
+                        max_efficiency,
+                        main_stat,
+                        main_stat_value,
+                        innate_stat,
+                        innate_stat_value,
+                        substat_1,
+                        substat_1_value,
+                        substat_2,
+                        substat_2_value,
+                        substat_3,
+                        substat_3_value,
+                        substat_4,
+                        substat_4_value
+                      ) VALUES (
+                        :run,
+                        :id,
+                        :type,
+                        :slot,
+                        :stars,
+                        :ancient,
+                        :quality,
+                        :value,
+                        :efficiency,
+                        :max_efficiency,
+                        :main_stat,
+                        :main_stat_value,
+                        :innate_stat,
+                        :innate_stat_value,
+                        :substat_1,
+                        :substat_1_value,
+                        :substat_2,
+                        :substat_2_value,
+                        :substat_3,
+                        :substat_3_value,
+                        :substat_4,
+                        :substat_4_value
+                      );
+                    ");
+                    $statement->bindValue(":run", $id, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $rune->{"rune_id"}, SQLITE3_INTEGER);
+                    $statement->bindValue(":type", $rune->{"set_id"}, SQLITE3_INTEGER);
+                    $statement->bindValue(":slot", $rune->{"slot_no"}, SQLITE3_INTEGER);
+                    $statement->bindValue(":value", $rune->{"sell_value"}, SQLITE3_INTEGER);
+                    if ($rune->{"class"} > 10){
+                        // Ancient rune
+                        $statement->bindValue(":ancient", 1, SQLITE3_INTEGER);
+                        $statement->bindValue(":stars", intval($rune->{"class"}) - 10, SQLITE3_INTEGER);
+                        $statement->bindValue(":quality", intval($rune->{"rank"}) - 10, SQLITE3_INTEGER);
+                    }
+                    else{
+                        // Non Ancient rune
+                        $statement->bindValue(":ancient", 0, SQLITE3_INTEGER);
+                        $statement->bindValue(":stars", $rune->{"class"}, SQLITE3_INTEGER);
+                        $statement->bindValue(":quality", $rune->{"rank"}, SQLITE3_INTEGER);
+                    }
+                    $efficiency = calculate_efficiency($rune);
+                    $max_efficiency = calculate_maximum_efficiency($rune);
+                    $statement->bindValue(":efficiency", $efficiency, SQLITE3_FLOAT);
+                    $statement->bindValue(":max_efficiency", $max_efficiency, SQLITE3_FLOAT);
+                    $statement->bindValue(":main_stat", $rune->{"pri_eff"}[0], SQLITE3_INTEGER);
+                    $statement->bindValue(":main_stat_value", $rune->{"pri_eff"}[1], SQLITE3_INTEGER);
+                    if ($rune->{"prefix_eff"}[0] > 0){
+                        $statement->bindValue(":innate_stat", $rune->{"prefix_eff"}[0], SQLITE3_INTEGER);
+                        $statement->bindValue(":innate_stat_value", $rune->{"prefix_eff"}[1], SQLITE3_INTEGER);
+                    }
+                    else{
+                        $statement->bindValue(":innate_stat", null, SQLITE3_INTEGER);
+                        $statement->bindValue(":innate_stat_value", 0, SQLITE3_INTEGER);
+                    }
+                    if (sizeof($rune->{"sec_eff"}) > 0){
+                        $statement->bindValue(":substat_1", $rune->{"sec_eff"}[0][0], SQLITE3_INTEGER);
+                        $statement->bindValue(":substat_1_value", $rune->{"sec_eff"}[0][1], SQLITE3_INTEGER);
+                    }
+                    else{
+                        $statement->bindValue(":substat_1", null, SQLITE3_INTEGER);
+                        $statement->bindValue(":substat_1_value", 0, SQLITE3_INTEGER);
+                    }
+                    if (sizeof($rune->{"sec_eff"}) > 1){
+                        $statement->bindValue(":substat_2", $rune->{"sec_eff"}[1][0]);
+                        $statement->bindValue(":substat_2_value", $rune->{"sec_eff"}[1][1]);
+                    }
+                    else{
+                        $statement->bindValue(":substat_2", null, SQLITE3_INTEGER);
+                        $statement->bindValue(":substat_2_value", 0, SQLITE3_INTEGER);
+                    }
+                    if (sizeof($rune->{"sec_eff"}) > 2){
+                        $statement->bindValue(":substat_3", $rune->{"sec_eff"}[2][0], SQLITE3_INTEGER);
+                        $statement->bindValue(":substat_3_value", $rune->{"sec_eff"}[2][1], SQLITE3_INTEGER);
+                    }
+                    else{
+                        $statement->bindValue(":substat_3", null, SQLITE3_INTEGER);
+                        $statement->bindValue(":substat_3_value", 0, SQLITE3_INTEGER);
+                    }
+                    if (sizeof($rune->{"sec_eff"}) > 3){
+                        $statement->bindValue(":substat_4", $rune->{"sec_eff"}[3][0], SQLITE3_INTEGER);
+                        $statement->bindValue(":substat_4_value", $rune->{"sec_eff"}[3][1], SQLITE3_INTEGER);
+                    }
+                    else{
+                        $statement->bindValue(":substat_4", null, SQLITE3_INTEGER);
+                        $statement->bindValue(":substat_4_value", 0, SQLITE3_INTEGER);
+                    }
+                    $statement->execute();
+                    break;
+            }
+        }
+
+        // Parse party
+        $leader = 1;
+        foreach ($response->{"unit_list"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO run_party 
+                (run, unit, k_unit, leader, front)
+              VALUES
+                (:run, :unit, :k_unit, :leader, :front);"
+            );
+            $statement->bindValue(":run", $id, SQLITE3_INTEGER);
+            $statement->bindValue(":unit", $unit->{"unit_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":k_unit", $unit->{"unit_master_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":leader", $leader, SQLITE3_INTEGER);
+            $leader = 0;
+            $statement->bindValue(":front", 0, SQLITE3_INTEGER);
+            $statement->execute();
+        }
+
+        // Return success
+        return "201 Created.";
+    }
+
+    /**
+     * Parses the JSONs relateds to a Dimension Hole run and saves the data.
+     *
+     * @param resource $db Database connection.
+     * @param int $uid Player UID.
+     * @param int $id Run ID.
+     * @param mixed[] $json List of received JSONs.
+     */
+    function log_dimension_hole_run($db, $id, $uid, $json){
+        // Only the result request and response JSONs are needed:
+        $response = $json["result_res"];
+        if ($json["result_req"] === null){
+            return ("400 Result request is required to parse Dimension Hole runs.");
+        }
+
+        // Prepare the "run" insert.
+        $statement = $db->prepare("
+          INSERT INTO run (
+            uid,
+            id,
+            dtime,
+            area_type,
+            area,
+            stage,
+            difficulty,
+            win,
+            time,
+            mana,
+            energy,
+            crystal,
+            guild_points,
+            honor_points,
+            helper,
+            score,
+            rank
+          ) VALUES (
+            :uid,
+            :id,
+            :dtime,
+            :area_type,
+            :area,
+            :stage,
+            :difficulty,
+            :win,
+            :time,
+            :mana,
+            :energy,
+            :crystal,
+            :guild_points,
+            :honor_points,
+            :helper,
+            :score,
+            :rank
+          );
+        ");
+
+        // Bind data and insert
+        $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+        $statement->bindValue(":id", $id, SQLITE3_INTEGER);
+        $statement->bindValue(":dtime", $response->{"tvaluelocal"}, SQLITE3_TEXT);
+        $statement->bindValue(":area_type", AREA_TYPE_ID::DIMENSIONAL_HOLE, SQLITE3_INTEGER);
+        $statement->bindValue(":area", $response->{"dungeon_id"}, SQLITE3_INTEGER);
+        $statement->bindValue(":stage", $response->{"difficulty"}, SQLITE3_INTEGER); // Difficulty is stage
+        $statement->bindValue(":difficulty", null, SQLITE3_INTEGER);
+        $statement->bindValue(":win", $response->{"win_lose"}, SQLITE3_INTEGER);
+        $statement->bindValue(":time", $response->{"clear_time"}->{"current_time"}, SQLITE3_INTEGER);
+        if (array_key_exists("mana", $response->{"reward"})){
+            $statement->bindValue(":mana", $response->{"reward"}->{"mana"}, SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":mana", 0, SQLITE3_INTEGER);
+        }
+        if (array_key_exists("energy", $response->{"reward"})){
+            $statement->bindValue(":energy", $response->{"reward"}->{"energy"}, SQLITE3_INTEGER);
+        }
+        $statement->bindValue(":energy", 0, SQLITE3_INTEGER);
+        if (array_key_exists("crystal", $response->{"reward"})){
+            $statement->bindValue(":crystal", $response->{"reward"}->{"crystal"}, SQLITE3_INTEGER);
+        }
+        else{
+            $statement->bindValue(":crystal", 0, SQLITE3_INTEGER);
+        }
+        $statement->bindValue(":guild_points", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":honor_points", 0, SQLITE3_INTEGER);
+        $statement->bindValue(":helper", 0, SQLITE3_INTEGER); // TODO Where is this information??
+        $statement->bindValue(":score", null, SQLITE3_INTEGER);
+        $statement->bindValue(":rank", null, SQLITE3_TEXT);
+        $statement->execute();
+
+        // Parse various types of reward
+        if (array_key_exists("changed_item_list", $response)){
+            foreach ($response->{"changed_item_list"} as $drop){
+                $reward_type = $drop->{"type"};
+                switch ($reward_type){
+                    case INVENTORY_TYPE_ID::CRAFT_STUFF:
+                        $statement = $db->prepare("INSERT INTO run_drop_item (run, item, amount) VALUES (:run, :item, :amount);");
+                        $statement->bindValue(":run", $id, SQLITE3_INTEGER);
+                        $statement->bindValue(":item", $drop->{"view"}->{"item_master_id"}, SQLITE3_INTEGER);
+                        $statement->bindValue(":amount", $drop->{"view"}->{"item_quantity"}, SQLITE3_INTEGER);
+                        $statement->execute();
+                        break;
+                    case INVENTORY_TYPE_ID::RUNE:
+                        // TODO: Investigate with no awaken dungeons
+                        /*$rune = $response->{"changed_item_list"}[0]->{"info"};
+                        $statement = $db->prepare("
+                          INSERT INTO run_drop_rune (
+                            run,
+                            id,
+                            type,
+                            slot,
+                            stars,
+                            ancient,
+                            quality,
+                            value,
+                            efficiency,
+                            max_efficiency,
+                            main_stat,
+                            main_stat_value,
+                            innate_stat,
+                            innate_stat_value,
+                            substat_1,
+                            substat_1_value,
+                            substat_2,
+                            substat_2_value,
+                            substat_3,
+                            substat_3_value,
+                            substat_4,
+                            substat_4_value
+                          ) VALUES (
+                            :run,
+                            :id,
+                            :type,
+                            :slot,
+                            :stars,
+                            :ancient,
+                            :quality,
+                            :value,
+                            :efficiency,
+                            :max_efficiency,
+                            :main_stat,
+                            :main_stat_value,
+                            :innate_stat,
+                            :innate_stat_value,
+                            :substat_1,
+                            :substat_1_value,
+                            :substat_2,
+                            :substat_2_value,
+                            :substat_3,
+                            :substat_3_value,
+                            :substat_4,
+                            :substat_4_value
+                        );
+                        ");
+                        $statement->bindValue(":run", $id, SQLITE3_INTEGER);
+                        $statement->bindValue(":id", $rune->{"rune_id"}, SQLITE3_INTEGER);
+                        $statement->bindValue(":type", $rune->{"set_id"}, SQLITE3_INTEGER);
+                        $statement->bindValue(":slot", $rune->{"slot_no"}, SQLITE3_INTEGER);
+                        $statement->bindValue(":value", $rune->{"sell_value"}, SQLITE3_INTEGER);
+                        if ($rune->{"class"} > 10){
+                            // Ancient rune
+                            $statement->bindValue(":ancient", 1, SQLITE3_INTEGER);
+                            $statement->bindValue(":stars", intval($rune->{"class"}) - 10, SQLITE3_INTEGER);
+                            $statement->bindValue(":quality", intval($rune->{"rank"}) - 10, SQLITE3_INTEGER);
+                        }
+                        else{
+                            // Non Ancient rune
+                            $statement->bindValue(":ancient", 0, SQLITE3_INTEGER);
+                            $statement->bindValue(":stars", $rune->{"class"}, SQLITE3_INTEGER);
+                            $statement->bindValue(":quality", $rune->{"rank"}, SQLITE3_INTEGER);
+                        }
+                        $efficiency = calculate_efficiency($rune);
+                        $max_efficiency = calculate_maximum_efficiency($rune);
+                        $statement->bindValue(":efficiency", $efficiency, SQLITE3_FLOAT);
+                        $statement->bindValue(":max_efficiency", $max_efficiency, SQLITE3_FLOAT);
+                        $statement->bindValue(":main_stat", $rune->{"pri_eff"}[0], SQLITE3_INTEGER);
+                        $statement->bindValue(":main_stat_value", $rune->{"pri_eff"}[1], SQLITE3_INTEGER);
+                        if ($rune->{"prefix_eff"}[0] > 0){
+                            $statement->bindValue(":innate_stat", $rune->{"prefix_eff"}[0], SQLITE3_INTEGER);
+                            $statement->bindValue(":innate_stat_value", $rune->{"prefix_eff"}[1], SQLITE3_INTEGER);
+                        }
+                        else{
+                            $statement->bindValue(":innate_stat", null, SQLITE3_INTEGER);
+                            $statement->bindValue(":innate_stat_value", 0, SQLITE3_INTEGER);
+                        }
+                        if (sizeof($rune->{"sec_eff"}) > 0){
+                            $statement->bindValue(":substat_1", $rune->{"sec_eff"}[0][0], SQLITE3_INTEGER);
+                            $statement->bindValue(":substat_1_value", $rune->{"sec_eff"}[0][1], SQLITE3_INTEGER);
+                        }
+                        else{
+                            $statement->bindValue(":substat_1", null, SQLITE3_INTEGER);
+                            $statement->bindValue(":substat_1_value", 0, SQLITE3_INTEGER);
+                        }
+                        if (sizeof($rune->{"sec_eff"}) > 1){
+                            $statement->bindValue(":substat_2", $rune->{"sec_eff"}[1][0], SQLITE3_INTEGER);
+                            $statement->bindValue(":substat_2_value", $rune->{"sec_eff"}[1][1], SQLITE3_INTEGER);
+                        }
+                        else{
+                            $statement->bindValue(":substat_2", null, SQLITE3_INTEGER);
+                            $statement->bindValue(":substat_2_value", 0, SQLITE3_INTEGER);
+                        }
+                        if (sizeof($rune->{"sec_eff"}) > 2){
+                            $statement->bindValue(":substat_3", $rune->{"sec_eff"}[2][0], SQLITE3_INTEGER);
+                            $statement->bindValue(":substat_3_value", $rune->{"sec_eff"}[2][1], SQLITE3_INTEGER);
+                        }
+                        else{
+                            $statement->bindValue(":substat_3", null, SQLITE3_INTEGER);
+                            $statement->bindValue(":substat_3_value", 0, SQLITE3_INTEGER);
+                        }
+                        if (sizeof($rune->{"sec_eff"}) > 3){
+                            $statement->bindValue(":substat_4", $rune->{"sec_eff"}[3][0], SQLITE3_INTEGER);
+                            $statement->bindValue(":substat_4_value", $rune->{"sec_eff"}[3][1], SQLITE3_INTEGER);
+                        }
+                        else{
+                            $statement->bindValue(":substat_4", null, SQLITE3_INTEGER);
+                            $statement->bindValue(":substat_4_value", 0, SQLITE3_INTEGER);
+                        }
+                        $statement->execute();*/
+                        break;
+                }
+            }
+        }
+
+        // Parse party
+        $leader = 1;
+        foreach ($response->{"unit_list"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO run_party 
+                (run, unit, k_unit, leader, front)
+              VALUES
+                (:run, :unit, :k_unit, :leader, :front);"
+            );
+            $statement->bindValue(":run", $id, SQLITE3_INTEGER);
+            $statement->bindValue(":unit", $unit->{"unit_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":k_unit", $unit->{"unit_master_id"}, SQLITE3_INTEGER);
+            $statement->bindValue(":leader", $leader, SQLITE3_INTEGER);
+            $leader = 0;
+            $statement->bindValue(":front", 0, SQLITE3_INTEGER);
+            $statement->execute();
+        }
+
+        // Return success
+        return "201 Created.";
+    }
+
+    global $db;
+
+    try{
+
+        header("Content-type: application/json; charset=utf-8");
+
+        // Get put data
+        parse_str(file_get_contents("php://input"), $_POST);
+
+        // Check API key.
+        if (!array_key_exists("key", $_POST)){
+            header("HTTP/1.1 400 API key not received.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 API key not received.");
+            return 400;
+        }
+        $key = $_POST["key"];
+
+        // Get all JSON files
+        $json = [
+            "start_req" => null,
+            "start_res" => null,
+            "result_req" => null,
+            "result_res" => null,
+        ];
+
+        // Result response - mandatory
+        if (!array_key_exists("result_response", $_POST)){
+            header("HTTP/1.1 400 No result response data received.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 No result response data received.");
+            return 400;
+        }
+        $data_json = json_decode($_POST["result_response"]);
+        if ($data_json === null){
+            header("HTTP/1.1 400 Result response data is no valid JSON.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 Result response data is no valid JSON.");
+            return 400;
+        }
+        $json["result_res"] = $data_json;
+
+        // Result request - optional
+        if (array_key_exists("result_request", $_POST)){
+            $data_raw = $_POST["result_request"];
+            if ($data_raw != null && $data_raw != false){
+                $data_json = json_decode($data_raw);
+                if ($data_json != null){
+                    $json["result_req"] = $data_json;
+                }
+            }
+        }
+        // Start result - optional
+        if (array_key_exists("start_response", $_POST)){
+            $data_raw = $_POST["start_response"];
+            if ($data_raw != null && $data_raw != false){
+                $data_json = json_decode($data_raw);
+                if ($data_json != null){
+                    $json["start_res"] = $data_json;
+                }
+            }
+        }
+        // Start result - optional
+        if (array_key_exists("start_request", $_POST)){
+            $data_raw = $_POST["start_request"];
+            if ($data_raw != null && $data_raw != false){
+                $data_json = json_decode($data_raw);
+                if ($data_json != null){
+                    $json["start_req"] = $data_json;
+                }
+            }
+        }
+
+        // Extract basic data
+        $uid = $json["result_res"]->{"wizard_info"}->{"wizard_id"};
+        $command = $json["result_res"]->{"command"};
+
+        // Authenticate
+        $statement = $db->prepare("SELECT COUNT(uid) AS c FROM player WHERE uid = :uid AND api_key = :api_key;");
+        $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+        $statement->bindValue(":api_key", $key, SQLITE3_TEXT);
+        if (1 != $statement->execute()->fetchArray(SQLITE3_ASSOC)["c"]){
+            header("HTTP/1.1 401 Invalid credentials.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 401 Invalid credentials.");
+            return 401;
+        }
+
+        // Check for implemented command
+        $accepted_commands = [
+            "BattleDungeonResult_V2", // Dungeon Run
+            "BattleDimensionHoleDungeonResult_v2", // Dimension Hole Run
+            "BattleRiftOfWorldsRaidResult", // Rift Raid Run
+            "BattleScenarioResult", // Scenario Run
+            "BattleArenaResult", // Arena Run
+            "BattleDarkPortalResult" // Dimensinal Rift Run
+        ];
+        if (!in_array($command, $accepted_commands)){
+            header("HTTP/1.1 405 Command not implemented.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 405 Command not implemented.");
+            return 405;
+        }
+
+        // Check if already in DB
+        $dtime = $json["result_res"]->{"tvaluelocal"};
+        $statement = $db->prepare("SELECT count(id) AS c FROM run WHERE uid = :uid AND dtime = :dtime;");
+        $statement->bindValue(":uid", $uid, SQLITE3_INTEGER);
+        $statement->bindValue(":dtime", $dtime, SQLITE3_TEXT);
+        if (0 != $statement->execute()->fetchArray(SQLITE3_ASSOC)["c"]){
+            header("HTTP/1.1 409 Run already in database.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 409 Run already in database.");
+            return 409;
+        }
+
+        // Get new ID
+        $q = $db->query("SELECT max(id) + 1 AS id FROM run;");
+        $r = $q->fetchArray(SQLITE3_ASSOC);
+        if ($r){
+            $id = $r["id"];
+        }
+        else{
+            $id = 1;
+        }
+
+        // Run parsing function
+        $result = "500 Unknown Error";
+        switch($command){
+            case "BattleDungeonResult_V2":
+                $result = log_cairos_run($db, $id, $uid, $json);
+                break;
+            case "BattleDimensionHoleDungeonResult_v2":
+                $result = log_dimension_hole_run($db, $id, $uid, $json);
+                break;
+            case "BattleRiftOfWorldsRaidResult":
+                $result = log_rift_raid_run($db, $id, $uid, $json);
+                break;
+            case "BattleScenarioResult":
+                $result = log_scenario_run($db, $id, $uid, $json);
+                break;
+            case "BattleArenaResult":
+                $result = log_arena_run($db, $id, $uid, $json);
+                break;
+            case "BattleDarkPortalResult":
+                $result = log_dark_portal_run($db, $id, $uid, $json);
+                break;
+        }
+
+        // Process result
+        $status_code = intval(substr($result, 0, 3));
+        $status_message = substr($result, 4);
+        if ($status_code == 0 || $status_code < 100 || $status_code > 599){
+            header("HTTP/1.1 500 Unknown error.");
+            syslog(LOG_ERR, "[APIv3] HTTP/1.1 500 Unknown error.");
+            return 500;
+        }
+        else{
+            header("HTTP/1.1 $status_code $status_message");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 $status_code $status_message");
+            return $status_code;
+        }
+    }
+    catch(Exception $e) {
+        header("HTTP/1.1 500 Unexpeced error: " . $e->getMessage());
+        syslog(LOG_ERR, "[APIv3] HTTP/1.1 500 Unexpected error: " . $e->getMessage());
+        return 500;
+    }
+?>

+ 455 - 0
application/API/v3/units/GET.php

@@ -0,0 +1,455 @@
+<?php
+    /**
+     * Unit getter script.
+     *
+     * Exposes an API to rerieve unit info.
+     *
+     * Optional query parameters are:
+     *  - id: Unit ID.
+     *
+     * @category API
+     */
+
+    global $db;
+
+    /**
+     * Gets info on a unit.
+     *
+     * @param resource $db Database connection.
+     * @param int $id Unit identifier
+     * @return mixed[] Content.
+     */
+    function get_unit($db, $id){
+        $result = [
+            "status" => 404,
+            "message" => "Unit not found",
+            "content" => ""
+        ];
+        $statement = $db->prepare("
+          SELECT
+            k_unit.id AS id,
+            k_unit.name AS name,
+            k_unit.family AS family,
+            k_unit.element AS element,
+            k_element.name AS element_name,
+            k_unit.archetype AS archetype,
+            k_archetype.name AS archetype_name,
+            k_unit.base_stars AS base_stars,
+            k_unit.natural_stars AS natural_stars,
+            k_unit.obtainable AS obtainable,
+            k_unit.awakens_from AS awakens_from,
+            k_unit.awakens_to AS awakens_to,
+            k_unit.homunculus AS homunculus,
+            k_unit.hp AS hp,
+            k_unit.attack AS attack,
+            k_unit.defense AS defense,
+            k_unit.speed AS speed,
+            k_unit.crit_rate AS crit_rate,
+            k_unit.crit_damage AS crit_damage,
+            k_unit.accuracy AS accuracy,
+            k_unit.resistance AS resistance,
+            k_unit.leader_skill AS leader_skill
+          FROM
+            k_unit,
+            k_element,
+            k_archetype
+          WHERE
+            k_unit.element = k_element.id AND
+            k_unit.archetype = k_archetype.id AND
+            k_unit.id = :id ;
+        ");
+        $statement->bindValue(":id", $id, SQLITE3_INTEGER);
+        $q = $statement->execute();
+        $r = $q->fetchArray(SQLITE3_ASSOC);
+        if ($r){
+            $unit = [
+                "id" => $r["id"],
+                "url" => URL::BASE . "API/v3/units/" . $r["id"] . "/",
+                "name" => $r["name"],
+                "family" => $r["family"],
+                "image" => APPLICATION::img("UNIT", $r["id"]),
+                "element" => $r["element"],
+                "element_name" => $r["element_name"],
+                "archetype" => $r["archetype"],
+                "archetype_name" => $r["archetype_name"],
+                "stars" => $r["base_stars"],
+                "natural_stars" => $r["natural_stars"],
+                "obtainable" => (($r["obtainable"] == 1) ? (true) : (false)),
+                "awakens_from" => $r["awakens_from"],
+                "awakens_to" => $r["awakens_to"],
+                "homunculus" => (($r["homunculus"] == 1) ? (true) : (false)),
+                "hp" => $r["hp"],
+                "attack" => $r["attack"],
+                "defense" => $r["defense"],
+                "speed" => $r["speed"],
+                "critical_rate" => $r["crit_rate"],
+                "critical_damage" => $r["crit_damage"],
+                "accuracy" => $r["accuracy"],
+                "resistance" => $r["resistance"],
+                "leader_skill" => null,
+                "skills" => [],
+            ];
+
+            // Leader_skill
+            if ($r["leader_skill"] != null){
+                $statement_leader = $db->prepare("
+                SELECT
+                    k_leader_skill.id AS id,
+                    k_leader_skill.stat AS stat,
+                    k_stat.name AS stat_name,
+                    k_leader_skill.amount AS amount,
+                    k_leader_skill.area AS area,
+                    k_effect_area.name AS area_name,
+                    k_leader_skill.element AS element,
+                    k_element.name AS element_name
+                FROM
+                    k_leader_skill,
+                    k_stat,
+                    k_effect_area,
+                    k_element
+                WHERE
+                    k_leader_skill.id = :id AND
+                    k_leader_skill.stat = k_stat.id AND
+                    k_leader_skill.area = k_effect_area.id AND
+                    k_leader_skill.element = k_element.id;
+                ");
+                $statement_leader->bindValue(":id", $r["leader_skill"], SQLITE3_INTEGER);
+                $q_leader = $statement_leader->execute();
+                $r_leader = $q_leader->fetchArray(SQLITE3_ASSOC);
+                if ($r_leader){
+                    $description = "Increases the ";
+                    switch ($r_leader["stat"]){
+                        case STAT_ID::ATK:
+                            $description .= "attack power";
+                            break;
+                        case STAT_ID::DEF:
+                            $description .= "defense";
+                            break;
+                        case STAT_ID::HP:
+                            $description .= "HP";
+                            break;
+                        case STAT_ID::SPD:
+                            $description .= "attack speed";
+                            break;
+                        case STAT_ID::CRIT_RATE:
+                            $description .= "critical rate";
+                            break;
+                        case STAT_ID::CRIT_DMG:
+                            $description .= "critical damage";
+                            break;
+                        case STAT_ID::ACCURACY:
+                            $description .= "accuracy";
+                            break;
+                        case STAT_ID::RESIST:
+                            $description .= "resistance";
+                            break;
+                    }
+                    $description .= " of ally monsters ";
+                    switch ($r_leader["element"]){
+                        case ELEMENT_ID::WATER:
+                            $description .= " with Water attribute ";
+                            break;
+                        case ELEMENT_ID::FIRE:
+                            $description .= " with Fire attribute ";
+                            break;
+                        case ELEMENT_ID::WIND:
+                            $description .= " with Wind attribute ";
+                            break;
+                        case ELEMENT_ID::LIGHT:
+                            $description .= " with Light attribute ";
+                            break;
+                        case ELEMENT_ID::DARK:
+                            $description .= " with Dark attribute ";
+                            break;
+                    }
+                    $description = $description . "by " . $r_leader["amount"] . "%";
+                    switch ($r_leader["area"]){
+                        case EFFECT_AREA_ID::DUNGEON:
+                            $description .= " in the Dungeons";
+                            break;
+                        case EFFECT_AREA_ID::ARENA:
+                            $description .= " in the Arena";
+                            break;
+                        case EFFECT_AREA_ID::GUILD:
+                            $description .= " in Guild content";
+                            break;
+                    }
+                    $description = str_replace("  ", " ", $description) . ".";
+                    $unit["leader_skill"] = [
+                        "id" => $r_leader["id"],
+                        "stat" => $r_leader["stat"],
+                        "stat_name" => $r_leader["stat_name"],
+                        "increase" => $r_leader["amount"],
+                        "area" => $r_leader["area"],
+                        "area_name" => $r_leader["area_name"],
+                        "element" => $r_leader["element"],
+                        "element_name" => $r_leader["element_name"],
+                        "description" => $description
+                    ];
+                }
+            }
+
+            // Skill
+            $statement_skills = $db->prepare("
+              SELECT
+                k_skill.id AS id,
+                k_skill.slot AS slot,
+                k_skill.name AS name,
+                k_skill.description AS description,
+                k_skill.passive AS passive,
+                k_skill.cooltime AS cooltime,
+                k_skill.hits AS hits,
+                k_skill.aoe AS aoe,
+                k_skill.multiplier_formula AS multiplier_formula,
+                k_skill.max_level AS max_level
+              FROM
+                k_skill,
+                k_unit_skill
+              WHERE
+                k_skill.id = k_unit_skill.skill AND
+                k_unit_skill.unit = :id 
+              ORDER BY slot;
+            ");
+            $statement_skills->bindValue(":id", $id, SQLITE3_INTEGER);
+            $q_skills = $statement_skills->execute();
+            while ($r_skills = $q_skills->fetchArray(SQLITE3_ASSOC)){
+                $skill = [
+                    "id" => $r_skills["id"],
+                    "slot" => $r_skills["slot"],
+                    "name" => $r_skills["name"],
+                    "description" => $r_skills["description"],
+                    "image" => APPLICATION::img("SKILL", $r_skills["id"]),
+                    "passive" => (($r_skills["passive"] == 1) ? (true) : (false)),
+                    "cooltime" => $r_skills["cooltime"],
+                    "hits" => $r_skills["hits"],
+                    "aoe" => (($r_skills["aoe"] == 1) ? (true) : (false)),
+                    "effects" => [],
+                    "max_level" => $r_skills["max_level"],
+                    "upgrades" => [],
+                    "formula" => $r_skills["multiplier_formula"]
+                ];
+                // Skill levels
+                $statement_skill_levels = $db->prepare("
+                  SELECT
+                    level,
+                    description
+                  FROM k_skill_level
+                  WHERE
+                    level > 1 AND
+                    skill = :id
+                  ORDER BY level;
+                ");
+                $statement_skill_levels->bindValue(":id", $r_skills["id"], SQLITE3_INTEGER);
+                $q_skill_levels = $statement_skill_levels->execute();
+                while ($r_skill_levels = $q_skill_levels->fetchArray(SQLITE3_ASSOC)){
+                    $level = [
+                        "level" => $r_skill_levels["level"],
+                        "description" => $r_skill_levels["description"]
+                    ];
+                    array_push($skill["upgrades"], $level);
+                }
+                // Skill efffects
+                $statement_skill_effects = $db->prepare("
+                  SELECT
+                    k_effect.id AS id,
+                    k_effect.is_buff AS is_buff,
+                    k_effect.name AS name,
+                    k_effect.description AS description
+                  FROM
+                    k_effect,
+                    k_skill_effect
+                  WHERE
+                    k_skill_effect.skill = :id AND
+                    k_skill_effect.effect = k_effect.id
+                ");
+                $statement_skill_effects->bindValue(":id", $r_skills["id"], SQLITE3_INTEGER);
+                $q_skill_effects = $statement_skill_effects->execute();
+                while ($r_skill_effects = $q_skill_effects->fetchArray(SQLITE3_ASSOC)){
+                    $effect = [
+                        "id" => $r_skill_effects["id"],
+                        "buff" => (($r_skill_effects["is_buff"] == 1) ? (true) : (false)),
+                        "name" => $r_skill_effects["name"],
+                        "description" => $r_skill_effects["description"],
+                        "image" => APPLICATION::img("EFFECT", $r_skill_effects["id"]),
+                    ];
+                    array_push($skill["effects"], $effect);
+                }
+                array_push($unit["skills"], $skill);
+            }
+
+            // Add to array
+            $result["content"] = $unit;
+            $result["status"] = 200;
+            $result["message"] = "Success.";
+        }
+        return $result;
+    }
+
+    /**
+     * Gets a list of units, paginated.
+     *
+     * @param resource $db Database connection.
+     * @return mixed[] Content.
+     */
+    function get_unit_list($db){
+        $result = [
+            "status" => 200,
+            "message" => "Success.",
+            "content" => ""
+        ];
+
+        // Get filters
+        $parameters = "";
+        $filters = [
+            "page" => 1,
+            "per_page" => 30,
+            "name" => "",
+            "element" => "",
+            "archetype" => "",
+            "stars" => "",
+            "stars_lte" => "",
+            "stars_gte" => "",
+        ];
+
+        foreach (array_keys($filters) as $filter_key){
+            $filter = filter_input(INPUT_GET, $filter_key);
+            if ($filter != null && $filter !== false){
+                $filters[$filter_key] = $filter;
+                // Also, build the new url parameters
+                if ($filter_key != "page"){
+                    $parameters .= ("&" . $filter_key . "=" . $filter);
+                }
+            }
+        }
+
+        // Fix some filters
+        if (intval($filters["page"]) < 1){
+            $filters["page"] = 1;
+        }
+        if (intval($filters["per_page"]) < 1){
+            $filters["per_page"] = 1;
+        }
+        elseif (intval($filters["per_page"]) > 100){
+            $filters["per_page"] = 100;
+        }
+
+        // Build the where clause
+        $where = "
+          k_unit.element = k_element.id AND
+          k_unit.archetype = k_archetype.id
+        ";
+        if (strlen($filters["name"]) > 0){
+            $where .= " AND upper(k_unit.name) LIKE upper(:name) ";
+        }
+        if (strlen($filters["element"]) > 0){
+            $where .= " AND (k_element.id = :element OR upper(k_element.name) = upper(:element)) ";
+        }
+        if (strlen($filters["archetype"]) > 0){
+            $where .= " AND (k_archetype.id = :archetype OR upper(k_archetype.name) = upper(:archetype)) ";
+        }
+        if (strlen($filters["stars"]) > 0){
+            $where .= " AND k_unit.base_stars = :stars ";
+        }
+        if (strlen($filters["stars_lte"]) > 0){
+            $where .= " AND k_unit.base_stars <= :stars_lte ";
+        }
+        if (strlen($filters["stars_gte"]) > 0){
+            $where .= " AND k_unit.base_stars >= :stars_gte ";
+        }
+
+        // Get total
+        $statement = $db->prepare("
+          SELECT count(k_unit.id) AS c
+          FROM
+            k_unit,
+            k_element,
+            k_archetype
+          WHERE $where ;
+        ");
+        $statement->bindValue(":name", "%" . $filters["name"] . "%", SQLITE3_TEXT);
+        $statement->bindValue(":element", $filters["element"], SQLITE3_TEXT);
+        $statement->bindValue(":archetype", $filters["archetype"], SQLITE3_TEXT);
+        $statement->bindValue(":stars", $filters["stars"], SQLITE3_INTEGER);
+        $statement->bindValue(":stars_gte", $filters["stars_gte"], SQLITE3_INTEGER);
+        $statement->bindValue(":stars_lte", $filters["stars_lte"], SQLITE3_INTEGER);
+        $total = $statement->execute()->fetchArray(SQLITE3_ASSOC)["c"];
+
+        // Selet all units
+        $statement = $db->prepare("
+          SELECT k_unit.id AS id
+          FROM
+            k_unit,
+            k_element,
+            k_archetype
+          WHERE $where
+          LIMIT :limit
+          OFFSET :offset ;
+        ");
+        $statement->bindValue(":name", "%" . $filters["name"] . "%", SQLITE3_TEXt);
+        $statement->bindValue(":element", $filters["element"], SQLITE3_TEXT);
+        $statement->bindValue(":archetype", $filters["archetype"], SQLITE3_TEXT);
+        $statement->bindValue(":stars", $filters["stars"], SQLITE3_INTEGER);
+        $statement->bindValue(":stars_gte", $filters["stars_gte"], SQLITE3_INTEGER);
+        $statement->bindValue(":stars_lte", $filters["stars_lte"], SQLITE3_INTEGER);
+        $statement->bindValue(":limit", $filters["per_page"], SQLITE3_INTEGER);
+        $statement->bindValue(":offset", $filters["per_page"] * ($filters["page"] - 1), SQLITE3_INTEGER);
+        $q = $statement->execute();
+        $units = [];
+
+        // Loop and get unit info
+        while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+            $unit = get_unit($db, $r["id"]);
+            if ($unit["status"] == 200){
+                array_push($units, $unit["content"]);
+            }
+        };
+
+        // Calculate previous and next pages
+        if ($filters["page"] == 1){
+            $url_prev = null;
+        }
+        else{
+            $url_prev = URL::BASE . "API/v3/units/?page=" . ($filters["page"] - 1) . $parameters;
+        }
+        if ($filters["page"] < ceil($total / $filters["per_page"])){
+            $url_next = URL::BASE . "API/v3/units/?page=" . ($filters["page"] + 1) . $parameters;
+        }
+        else{
+            $url_next = null;
+        }
+        $content = [
+            "count" => $total,
+            "next" => $url_next,
+            "previous" => $url_prev,
+            "results" => $units
+        ];
+        $result["content"] = $content;
+        return $result;
+    }
+
+    try{
+        header("Content-type: application/json; charset=utf-8");
+        $result = [
+            "status" => 200,
+            "message" => "Success.",
+            "content" => ""
+        ];
+        // Get profile ID
+        /** @var mixed $query Request parameters, from API_CONTROLLER*/
+        if (strlen($query[0]) > 0){
+            $id = $query[0];
+            $result = get_unit($db, $id);
+        }
+        else{
+            $result = get_unit_list($db);
+        }
+        header("HTTP/1.1 " . $result["status"] . " " . $result["message"]);
+        echo(json_encode($result["content"]));
+        syslog(LOG_ERR, "HTTP/1.1 " . $result["status"] . " " . $result["message"]);
+        return $result["status"];
+    }
+    catch(Exception $e) {
+        header("HTTP/1.1 500 Unexpeced error: " . $e->getMessage());
+        syslog(LOG_ERR, "[APIv3] HTTP/1.1 500 Unexpected error: " . $e->getMessage());
+        return 500;
+    }

+ 1357 - 2
application/Constant.php

@@ -1019,9 +1019,9 @@
         const RAINBOWMON = 25;   # Possibly material monsters in general. Appears when wish returns a rainbowmon.
 
         /**
-         * Rune Craft.
+         * Enchantment.
          */
-        const RUNE_CRAFT = 27;
+        const ENCHANTMENT = 27;
 
         /**
          * Craft Stuff.
@@ -2763,6 +2763,1361 @@
         ];
     };
 
+    /**
+     * Rune main stat values constants.
+     *
+     * Includes the value of the main stats for type, stars, and level.
+     *
+     * @category Data
+     */
+    final class RUNE_SUBSTAT_MAX_VALUES{
+    
+        const SUBSTAT = [
+            RUNE_STAT_ID::HP => [
+                "1" => 300,
+                "2" => 525,
+                "3" => 825,
+                "4" => 1125,
+                "5" => 1500,
+                "6" => 1875
+            ],
+            RUNE_STAT_ID::HP_P => [
+                "1" => 10,
+                "2" => 15,
+                "3" => 25,
+                "4" => 30,
+                "5" => 35,
+                "6" => 40
+            ],
+            RUNE_STAT_ID::ATK => [
+                "1" => 20,
+                "2" => 25,
+                "3" => 40,
+                "4" => 50,
+                "5" => 75,
+                "6" => 100
+            ],
+            RUNE_STAT_ID::ATK_P => [
+                "1" => 10,
+                "2" => 15,
+                "3" => 25,
+                "4" => 30,
+                "5" => 35,
+                "6" => 40
+            ],
+            RUNE_STAT_ID::DEF => [
+                "1" => 20,
+                "2" => 25,
+                "3" => 40,
+                "4" => 50,
+                "5" => 75,
+                "6" => 100
+            ],
+            RUNE_STAT_ID::DEF_P => [
+                "1" => 10,
+                "2" => 15,
+                "3" => 25,
+                "4" => 30,
+                "5" => 35,
+                "6" => 40
+            ],
+            RUNE_STAT_ID::SPD => [
+                "1" => 5,
+                "2" => 10,
+                "3" => 15,
+                "4" => 20,
+                "5" => 25,
+                "6" => 30
+            ],
+            RUNE_STAT_ID::CRR => [
+                "1" => 5,
+                "2" => 10,
+                "3" => 15,
+                "4" => 20,
+                "5" => 25,
+                "6" => 30
+            ],
+            RUNE_STAT_ID::CRD => [
+                "1" => 10,
+                "2" => 15,
+                "3" => 20,
+                "4" => 25,
+                "5" => 30,
+                "6" => 35
+            ],
+            RUNE_STAT_ID::RES => [
+                "1" => 10,
+                "2" => 15,
+                "3" => 25,
+                "4" => 30,
+                "5" => 35,
+                "6" => 40
+            ],
+            RUNE_STAT_ID::ACC => [
+                "1" => 10,
+                "2" => 15,
+                "3" => 25,
+                "4" => 30,
+                "5" => 35,
+                "6" => 40
+            ]
+        ];
+
+        /**
+         * Values of the HP main stat [stars][level]
+         */
+        const HP = [
+            "1" => [
+                "0" => 40,
+                "1" => 85,
+                "2" => 130,
+                "3" => 175,
+                "4" => 220,
+                "5" => 265,
+                "6" => 310,
+                "7" => 355,
+                "8" => 400,
+                "9" => 445,
+                "10" => 490,
+                "11" => 535,
+                "12" => 580,
+                "13" => 625,
+                "14" => 670,
+                "15" => 804
+            ],
+            "2" => [
+                "0" => 70,
+                "1" => 130,
+                "2" => 190,
+                "3" => 250,
+                "4" => 310,
+                "5" => 370,
+                "6" => 430,
+                "7" => 490,
+                "8" => 550,
+                "9" => 610,
+                "10" => 670,
+                "11" => 730,
+                "12" => 790,
+                "13" => 850,
+                "14" => 910,
+                "15" => 1092
+            ],
+            "3" => [
+                "0" => 100,
+                "1" => 175,
+                "2" => 250,
+                "3" => 325,
+                "4" => 400,
+                "5" => 475,
+                "6" => 550,
+                "7" => 625,
+                "8" => 700,
+                "9" => 775,
+                "10" => 850,
+                "11" => 925,
+                "12" => 1000,
+                "13" => 1075,
+                "14" => 1150,
+                "15" => 1380
+            ],
+            "4" => [
+                "0" => 160,
+                "1" => 250,
+                "2" => 340,
+                "3" => 430,
+                "4" => 520,
+                "5" => 610,
+                "6" => 700,
+                "7" => 790,
+                "8" => 880,
+                "9" => 970,
+                "10" => 1060,
+                "11" => 1150,
+                "12" => 1240,
+                "13" => 1330,
+                "14" => 1420,
+                "15" => 1704
+            ],
+            "5" => [
+                "0" => 270,
+                "1" => 375,
+                "2" => 480,
+                "3" => 585,
+                "4" => 690,
+                "5" => 795,
+                "6" => 900,
+                "7" => 1005,
+                "8" => 1110,
+                "9" => 1215,
+                "10" => 1320,
+                "11" => 1425,
+                "12" => 1530,
+                "13" => 1635,
+                "14" => 1740,
+                "15" => 2088
+            ],
+            "6" => [
+                "0" => 360,
+                "1" => 480,
+                "2" => 600,
+                "3" => 720,
+                "4" => 840,
+                "5" => 960,
+                "6" => 1080,
+                "7" => 1200,
+                "8" => 1320,
+                "9" => 1440,
+                "10" => 1560,
+                "11" => 1680,
+                "12" => 1800,
+                "13" => 1920,
+                "14" => 2040,
+                "15" => 2448
+            ]
+        ];
+        
+        /**
+         * Values of the HP_P main stat[stars][level]
+         */
+        const HP_P = [
+            "1" => [
+                "0" => 1,
+                "1" => 2,
+                "2" => 3,
+                "3" => 4,
+                "4" => 5,
+                "5" => 6,
+                "6" => 7,
+                "7" => 8,
+                "8" => 9,
+                "9" => 10,
+                "10" => 11,
+                "11" => 12,
+                "12" => 13,
+                "13" => 14,
+                "14" => 15,
+                "15" => 18
+            ],
+            "2" => [
+                "0" => 2,
+                "1" => 3,
+                "2" => 4,
+                "3" => 5,
+                "4" => 6,
+                "5" => 7,
+                "6" => 8,
+                "7" => 9,
+                "8" => 10,
+                "9" => 11,
+                "10" => 12,
+                "11" => 13,
+                "12" => 14,
+                "13" => 15,
+                "14" => 16,
+                "15" => 19
+            ],
+            "3" => [
+                "0" => 4,
+                "1" => 6,
+                "2" => 8,
+                "3" => 10,
+                "4" => 12,
+                "5" => 14,
+                "6" => 16,
+                "7" => 18,
+                "8" => 20,
+                "9" => 22,
+                "10" => 24,
+                "11" => 26,
+                "12" => 28,
+                "13" => 30,
+                "14" => 32,
+                "15" => 38
+            ],
+            "4" => [
+                "0" => 5,
+                "1" => 7,
+                "2" => 9,
+                "3" => 11,
+                "4" => 13,
+                "5" => 16,
+                "6" => 18,
+                "7" => 20,
+                "8" => 22,
+                "9" => 24,
+                "10" => 27,
+                "11" => 29,
+                "12" => 31,
+                "13" => 33,
+                "14" => 36,
+                "15" => 43
+            ],
+            "5" => [
+                "0" => 8,
+                "1" => 10,
+                "2" => 12,
+                "3" => 15,
+                "4" => 17,
+                "5" => 20,
+                "6" => 22,
+                "7" => 24,
+                "8" => 27,
+                "9" => 29,
+                "10" => 32,
+                "11" => 34,
+                "12" => 37,
+                "13" => 40,
+                "14" => 43,
+                "15" => 51
+            ],
+            "6" => [
+                "0" => 11,
+                "1" => 14,
+                "2" => 17,
+                "3" => 20,
+                "4" => 23,
+                "5" => 26,
+                "6" => 29,
+                "7" => 32,
+                "8" => 35,
+                "9" => 38,
+                "10" => 41,
+                "11" => 44,
+                "12" => 47,
+                "13" => 50,
+                "14" => 53,
+                "15" => 63
+            ]
+        ];
+
+        /**
+         * Values of the ATK main stat[stars][level]
+         */
+        const ATK = [
+            "1" => [
+                "0" => 3,
+                "1" => 6,
+                "2" => 9,
+                "3" => 12,
+                "4" => 15,
+                "5" => 18,
+                "6" => 21,
+                "7" => 24,
+                "8" => 27,
+                "9" => 30,
+                "10" => 33,
+                "11" => 36,
+                "12" => 39,
+                "13" => 42,
+                "14" => 45,
+                "15" => 54
+            ],
+            "2" => [
+                "0" => 5,
+                "1" => 9,
+                "2" => 13,
+                "3" => 17,
+                "4" => 21,
+                "5" => 25,
+                "6" => 29,
+                "7" => 33,
+                "8" => 37,
+                "9" => 41,
+                "10" => 45,
+                "11" => 49,
+                "12" => 53,
+                "13" => 57,
+                "14" => 61,
+                "15" => 73
+            ],
+            "3" => [
+                "0" => 7,
+                "1" => 12,
+                "2" => 17,
+                "3" => 22,
+                "4" => 27,
+                "5" => 32,
+                "6" => 37,
+                "7" => 42,
+                "8" => 47,
+                "9" => 52,
+                "10" => 57,
+                "11" => 62,
+                "12" => 67,
+                "13" => 72,
+                "14" => 77,
+                "15" => 92
+            ],
+            "4" => [
+                "0" => 10,
+                "1" => 16,
+                "2" => 22,
+                "3" => 28,
+                "4" => 34,
+                "5" => 40,
+                "6" => 46,
+                "7" => 52,
+                "8" => 58,
+                "9" => 64,
+                "10" => 70,
+                "11" => 76,
+                "12" => 82,
+                "13" => 88,
+                "14" => 94,
+                "15" => 112
+            ],
+            "5" => [
+                "0" => 15,
+                "1" => 22,
+                "2" => 29,
+                "3" => 36,
+                "4" => 43,
+                "5" => 50,
+                "6" => 57,
+                "7" => 64,
+                "8" => 71,
+                "9" => 78,
+                "10" => 85,
+                "11" => 92,
+                "12" => 99,
+                "13" => 106,
+                "14" => 113,
+                "15" => 135
+            ],
+            "6" => [
+                "0" => 22,
+                "1" => 30,
+                "2" => 38,
+                "3" => 46,
+                "4" => 54,
+                "5" => 62,
+                "6" => 70,
+                "7" => 78,
+                "8" => 86,
+                "9" => 94,
+                "10" => 102,
+                "11" => 110,
+                "12" => 118,
+                "13" => 126,
+                "14" => 134,
+                "15" => 160
+            ]
+        ];
+
+        /**
+         * Values of the ATK_P main stat[stars][level]
+         */
+        const ATK_P = [
+            "1" => [
+                "0" => 1,
+                "1" => 2,
+                "2" => 3,
+                "3" => 4,
+                "4" => 5,
+                "5" => 6,
+                "6" => 7,
+                "7" => 8,
+                "8" => 9,
+                "9" => 10,
+                "10" => 11,
+                "11" => 12,
+                "12" => 13,
+                "13" => 14,
+                "14" => 15,
+                "15" => 18
+            ],
+            "2" => [
+                "0" => 2,
+                "1" => 3,
+                "2" => 4,
+                "3" => 5,
+                "4" => 6,
+                "5" => 7,
+                "6" => 8,
+                "7" => 9,
+                "8" => 10,
+                "9" => 11,
+                "10" => 12,
+                "11" => 13,
+                "12" => 14,
+                "13" => 15,
+                "14" => 16,
+                "15" => 19
+            ],
+            "3" => [
+                "0" => 4,
+                "1" => 6,
+                "2" => 8,
+                "3" => 10,
+                "4" => 12,
+                "5" => 14,
+                "6" => 16,
+                "7" => 18,
+                "8" => 20,
+                "9" => 22,
+                "10" => 24,
+                "11" => 26,
+                "12" => 28,
+                "13" => 30,
+                "14" => 32,
+                "15" => 38
+            ],
+            "4" => [
+                "0" => 5,
+                "1" => 7,
+                "2" => 9,
+                "3" => 11,
+                "4" => 13,
+                "5" => 16,
+                "6" => 18,
+                "7" => 20,
+                "8" => 22,
+                "9" => 24,
+                "10" => 27,
+                "11" => 29,
+                "12" => 31,
+                "13" => 33,
+                "14" => 36,
+                "15" => 43
+            ],
+            "5" => [
+                "0" => 8,
+                "1" => 10,
+                "2" => 12,
+                "3" => 15,
+                "4" => 17,
+                "5" => 20,
+                "6" => 22,
+                "7" => 24,
+                "8" => 27,
+                "9" => 29,
+                "10" => 32,
+                "11" => 34,
+                "12" => 37,
+                "13" => 40,
+                "14" => 43,
+                "15" => 51
+            ],
+            "6" => [
+                "0" => 11,
+                "1" => 14,
+                "2" => 17,
+                "3" => 20,
+                "4" => 23,
+                "5" => 26,
+                "6" => 29,
+                "7" => 32,
+                "8" => 35,
+                "9" => 38,
+                "10" => 41,
+                "11" => 44,
+                "12" => 47,
+                "13" => 50,
+                "14" => 53,
+                "15" => 63
+            ]
+        ];
+        
+        /**
+         * Values of the DEF main stat[stars][level]
+         */
+        const DEF = [
+            "1" => [
+                "0" => 3,
+                "1" => 6,
+                "2" => 9,
+                "3" => 12,
+                "4" => 15,
+                "5" => 18,
+                "6" => 21,
+                "7" => 24,
+                "8" => 27,
+                "9" => 30,
+                "10" => 33,
+                "11" => 36,
+                "12" => 39,
+                "13" => 42,
+                "14" => 45,
+                "15" => 54
+            ],
+            "2" => [
+                "0" => 5,
+                "1" => 9,
+                "2" => 13,
+                "3" => 17,
+                "4" => 21,
+                "5" => 25,
+                "6" => 29,
+                "7" => 33,
+                "8" => 37,
+                "9" => 41,
+                "10" => 45,
+                "11" => 49,
+                "12" => 53,
+                "13" => 57,
+                "14" => 61,
+                "15" => 73
+            ],
+            "3" => [
+                "0" => 7,
+                "1" => 12,
+                "2" => 17,
+                "3" => 22,
+                "4" => 27,
+                "5" => 32,
+                "6" => 37,
+                "7" => 42,
+                "8" => 47,
+                "9" => 52,
+                "10" => 57,
+                "11" => 62,
+                "12" => 67,
+                "13" => 72,
+                "14" => 77,
+                "15" => 92
+            ],
+            "4" => [
+                "0" => 10,
+                "1" => 16,
+                "2" => 22,
+                "3" => 28,
+                "4" => 34,
+                "5" => 40,
+                "6" => 46,
+                "7" => 52,
+                "8" => 58,
+                "9" => 64,
+                "10" => 70,
+                "11" => 76,
+                "12" => 82,
+                "13" => 88,
+                "14" => 94,
+                "15" => 112
+            ],
+            "5" => [
+                "0" => 15,
+                "1" => 22,
+                "2" => 29,
+                "3" => 36,
+                "4" => 43,
+                "5" => 50,
+                "6" => 57,
+                "7" => 64,
+                "8" => 71,
+                "9" => 78,
+                "10" => 85,
+                "11" => 92,
+                "12" => 99,
+                "13" => 106,
+                "14" => 113,
+                "15" => 135
+            ],
+            "6" => [
+                "0" => 22,
+                "1" => 30,
+                "2" => 38,
+                "3" => 46,
+                "4" => 54,
+                "5" => 62,
+                "6" => 70,
+                "7" => 78,
+                "8" => 86,
+                "9" => 94,
+                "10" => 102,
+                "11" => 110,
+                "12" => 118,
+                "13" => 126,
+                "14" => 134,
+                "15" => 160
+            ]
+        ];
+        
+        /**
+         * Values of the DEF_P main stat[stars][level]
+         */
+        const DEF_P = [
+            "1" => [
+                "0" => 1,
+                "1" => 2,
+                "2" => 3,
+                "3" => 4,
+                "4" => 5,
+                "5" => 6,
+                "6" => 7,
+                "7" => 8,
+                "8" => 9,
+                "9" => 10,
+                "10" => 11,
+                "11" => 12,
+                "12" => 13,
+                "13" => 14,
+                "14" => 15,
+                "15" => 18
+            ],
+            "2" => [
+                "0" => 2,
+                "1" => 3,
+                "2" => 4,
+                "3" => 5,
+                "4" => 6,
+                "5" => 7,
+                "6" => 8,
+                "7" => 9,
+                "8" => 10,
+                "9" => 11,
+                "10" => 12,
+                "11" => 13,
+                "12" => 14,
+                "13" => 15,
+                "14" => 16,
+                "15" => 19
+            ],
+            "3" => [
+                "0" => 4,
+                "1" => 6,
+                "2" => 8,
+                "3" => 10,
+                "4" => 12,
+                "5" => 14,
+                "6" => 16,
+                "7" => 18,
+                "8" => 20,
+                "9" => 22,
+                "10" => 24,
+                "11" => 26,
+                "12" => 28,
+                "13" => 30,
+                "14" => 32,
+                "15" => 38
+            ],
+            "4" => [
+                "0" => 5,
+                "1" => 7,
+                "2" => 9,
+                "3" => 11,
+                "4" => 13,
+                "5" => 16,
+                "6" => 18,
+                "7" => 20,
+                "8" => 22,
+                "9" => 24,
+                "10" => 27,
+                "11" => 29,
+                "12" => 31,
+                "13" => 33,
+                "14" => 36,
+                "15" => 43
+            ],
+            "5" => [
+                "0" => 8,
+                "1" => 10,
+                "2" => 12,
+                "3" => 15,
+                "4" => 17,
+                "5" => 20,
+                "6" => 22,
+                "7" => 24,
+                "8" => 27,
+                "9" => 29,
+                "10" => 32,
+                "11" => 34,
+                "12" => 37,
+                "13" => 40,
+                "14" => 43,
+                "15" => 51
+            ],
+            "6" => [
+                "0" => 11,
+                "1" => 14,
+                "2" => 17,
+                "3" => 20,
+                "4" => 23,
+                "5" => 26,
+                "6" => 29,
+                "7" => 32,
+                "8" => 35,
+                "9" => 38,
+                "10" => 41,
+                "11" => 44,
+                "12" => 47,
+                "13" => 50,
+                "14" => 53,
+                "15" => 63
+            ]
+        ];
+
+        /**
+         * Values of the SPD main stat[stars][level]
+         */
+        const SPD = [
+            "1" => [
+                "0" => 1,
+                "1" => 2,
+                "2" => 3,
+                "3" => 4,
+                "4" => 5,
+                "5" => 6,
+                "6" => 7,
+                "7" => 8,
+                "8" => 9,
+                "9" => 10,
+                "10" => 11,
+                "11" => 12,
+                "12" => 13,
+                "13" => 14,
+                "14" => 15,
+                "15" => 18
+            ],
+            "2" => [
+                "0" => 2,
+                "1" => 3,
+                "2" => 4,
+                "3" => 5,
+                "4" => 6,
+                "5" => 7,
+                "6" => 8,
+                "7" => 9,
+                "8" => 10,
+                "9" => 11,
+                "10" => 12,
+                "11" => 13,
+                "12" => 14,
+                "13" => 15,
+                "14" => 16,
+                "15" => 19
+            ],
+            "3" => [
+                "0" => 3,
+                "1" => 4,
+                "2" => 5,
+                "3" => 6,
+                "4" => 8,
+                "5" => 9,
+                "6" => 10,
+                "7" => 12,
+                "8" => 13,
+                "9" => 14,
+                "10" => 16,
+                "11" => 17,
+                "12" => 18,
+                "13" => 19,
+                "14" => 21,
+                "15" => 25
+            ],
+            "4" => [
+                "0" => 4,
+                "1" => 5,
+                "2" => 7,
+                "3" => 8,
+                "4" => 10,
+                "5" => 11,
+                "6" => 13,
+                "7" => 14,
+                "8" => 16,
+                "9" => 17,
+                "10" => 19,
+                "11" => 20,
+                "12" => 22,
+                "13" => 23,
+                "14" => 25,
+                "15" => 30
+            ],
+            "5" => [
+                "0" => 5,
+                "1" => 7,
+                "2" => 9,
+                "3" => 11,
+                "4" => 13,
+                "5" => 15,
+                "6" => 17,
+                "7" => 19,
+                "8" => 21,
+                "9" => 23,
+                "10" => 25,
+                "11" => 27,
+                "12" => 29,
+                "13" => 31,
+                "14" => 33,
+                "15" => 39
+            ],
+            "6" => [
+                "0" => 7,
+                "1" => 9,
+                "2" => 11,
+                "3" => 13,
+                "4" => 15,
+                "5" => 17,
+                "6" => 19,
+                "7" => 21,
+                "8" => 23,
+                "9" => 25,
+                "10" => 27,
+                "11" => 29,
+                "12" => 31,
+                "13" => 33,
+                "14" => 35,
+                "15" => 42
+            ]
+        ];
+
+        /**
+         * Values of the CRR main stat[stars][level]
+         */
+        const CRR = [
+            "1" => [
+                "0" => 1,
+                "1" => 2,
+                "2" => 3,
+                "3" => 4,
+                "4" => 5,
+                "5" => 6,
+                "6" => 7,
+                "7" => 8,
+                "8" => 9,
+                "9" => 10,
+                "10" => 11,
+                "11" => 12,
+                "12" => 13,
+                "13" => 14,
+                "14" => 15,
+                "15" => 18
+            ],
+            "2" => [
+                "0" => 2,
+                "1" => 3,
+                "2" => 4,
+                "3" => 5,
+                "4" => 6,
+                "5" => 7,
+                "6" => 8,
+                "7" => 9,
+                "8" => 10,
+                "9" => 11,
+                "10" => 12,
+                "11" => 13,
+                "12" => 14,
+                "13" => 15,
+                "14" => 16,
+                "15" => 19
+            ],
+            "3" => [
+                "0" => 3,
+                "1" => 5,
+                "2" => 7,
+                "3" => 9,
+                "4" => 11,
+                "5" => 13,
+                "6" => 15,
+                "7" => 17,
+                "8" => 19,
+                "9" => 21,
+                "10" => 23,
+                "11" => 25,
+                "12" => 27,
+                "13" => 29,
+                "14" => 31,
+                "15" => 37
+            ],
+            "4" => [
+                "0" => 4,
+                "1" => 6,
+                "2" => 8,
+                "3" => 11,
+                "4" => 13,
+                "5" => 15,
+                "6" => 17,
+                "7" => 19,
+                "8" => 22,
+                "9" => 24,
+                "10" => 26,
+                "11" => 28,
+                "12" => 30,
+                "13" => 33,
+                "14" => 35,
+                "15" => 41
+            ],
+            "5" => [
+                "0" => 5,
+                "1" => 7,
+                "2" => 10,
+                "3" => 12,
+                "4" => 15,
+                "5" => 17,
+                "6" => 19,
+                "7" => 22,
+                "8" => 24,
+                "9" => 27,
+                "10" => 29,
+                "11" => 31,
+                "12" => 34,
+                "13" => 36,
+                "14" => 39,
+                "15" => 47
+            ],
+            "6" => [
+                "0" => 7,
+                "1" => 10,
+                "2" => 13,
+                "3" => 16,
+                "4" => 19,
+                "5" => 22,
+                "6" => 25,
+                "7" => 28,
+                "8" => 31,
+                "9" => 34,
+                "10" => 37,
+                "11" => 40,
+                "12" => 43,
+                "13" => 46,
+                "14" => 49,
+                "15" => 58
+            ]
+        ];
+
+        /**
+         * Values of the CRD main stat[stars][level]
+         */
+        const CRD = [
+            "1" => [
+                "0" => 2,
+                "1" => 3,
+                "2" => 4,
+                "3" => 5,
+                "4" => 6,
+                "5" => 7,
+                "6" => 8,
+                "7" => 9,
+                "8" => 10,
+                "9" => 11,
+                "10" => 12,
+                "11" => 13,
+                "12" => 14,
+                "13" => 15,
+                "14" => 16,
+                "15" => 19
+            ],
+            "2" => [
+                "0" => 3,
+                "1" => 5,
+                "2" => 7,
+                "3" => 9,
+                "4" => 11,
+                "5" => 13,
+                "6" => 15,
+                "7" => 17,
+                "8" => 19,
+                "9" => 21,
+                "10" => 23,
+                "11" => 25,
+                "12" => 27,
+                "13" => 29,
+                "14" => 31,
+                "15" => 37
+            ],
+            "3" => [
+                "0" => 4,
+                "1" => 6,
+                "2" => 9,
+                "3" => 11,
+                "4" => 13,
+                "5" => 16,
+                "6" => 18,
+                "7" => 20,
+                "8" => 22,
+                "9" => 25,
+                "10" => 27,
+                "11" => 29,
+                "12" => 32,
+                "13" => 34,
+                "14" => 36,
+                "15" => 43
+            ],
+            "4" => [
+                "0" => 6,
+                "1" => 9,
+                "2" => 12,
+                "3" => 15,
+                "4" => 18,
+                "5" => 21,
+                "6" => 24,
+                "7" => 27,
+                "8" => 30,
+                "9" => 33,
+                "10" => 36,
+                "11" => 39,
+                "12" => 42,
+                "13" => 45,
+                "14" => 48,
+                "15" => 57
+            ],
+            "5" => [
+                "0" => 8,
+                "1" => 11,
+                "2" => 15,
+                "3" => 18,
+                "4" => 21,
+                "5" => 25,
+                "6" => 28,
+                "7" => 31,
+                "8" => 34,
+                "9" => 38,
+                "10" => 41,
+                "11" => 44,
+                "12" => 48,
+                "13" => 51,
+                "14" => 54,
+                "15" => 65
+            ],
+            "6" => [
+                "0" => 11,
+                "1" => 15,
+                "2" => 19,
+                "3" => 23,
+                "4" => 27,
+                "5" => 31,
+                "6" => 35,
+                "7" => 39,
+                "8" => 43,
+                "9" => 47,
+                "10" => 51,
+                "11" => 55,
+                "12" => 59,
+                "13" => 63,
+                "14" => 67,
+                "15" => 80
+            ]
+        ];
+
+        /**
+         * Values of the ACC main stat[stars][level]
+         */
+        const ACC = [
+            "1" => [
+                "0" => 1,
+                "1" => 2,
+                "2" => 3,
+                "3" => 4,
+                "4" => 5,
+                "5" => 6,
+                "6" => 7,
+                "7" => 8,
+                "8" => 9,
+                "9" => 10,
+                "10" => 11,
+                "11" => 12,
+                "12" => 13,
+                "13" => 14,
+                "14" => 15,
+                "15" => 18
+            ],
+            "2" => [
+                "0" => 2,
+                "1" => 3,
+                "2" => 4,
+                "3" => 5,
+                "4" => 6,
+                "5" => 7,
+                "6" => 8,
+                "7" => 9,
+                "8" => 10,
+                "9" => 11,
+                "10" => 12,
+                "11" => 13,
+                "12" => 14,
+                "13" => 15,
+                "14" => 16,
+                "15" => 19
+            ],
+            "3" => [
+                "0" => 4,
+                "1" => 6,
+                "2" => 8,
+                "3" => 10,
+                "4" => 12,
+                "5" => 14,
+                "6" => 16,
+                "7" => 18,
+                "8" => 20,
+                "9" => 22,
+                "10" => 24,
+                "11" => 26,
+                "12" => 28,
+                "13" => 30,
+                "14" => 32,
+                "15" => 38
+            ],
+            "4" => [
+                "0" => 6,
+                "1" => 8,
+                "2" => 10,
+                "3" => 13,
+                "4" => 15,
+                "5" => 17,
+                "6" => 19,
+                "7" => 21,
+                "8" => 24,
+                "9" => 26,
+                "10" => 28,
+                "11" => 30,
+                "12" => 32,
+                "13" => 35,
+                "14" => 37,
+                "15" => 44
+            ],
+            "5" => [
+                "0" => 9,
+                "1" => 11,
+                "2" => 14,
+                "3" => 16,
+                "4" => 19,
+                "5" => 21,
+                "6" => 23,
+                "7" => 26,
+                "8" => 28,
+                "9" => 31,
+                "10" => 33,
+                "11" => 35,
+                "12" => 38,
+                "13" => 40,
+                "14" => 43,
+                "15" => 51
+            ],
+            "6" => [
+                "0" => 12,
+                "1" => 15,
+                "2" => 18,
+                "3" => 21,
+                "4" => 24,
+                "5" => 27,
+                "6" => 30,
+                "7" => 33,
+                "8" => 36,
+                "9" => 39,
+                "10" => 42,
+                "11" => 45,
+                "12" => 48,
+                "13" => 51,
+                "14" => 54,
+                "15" => 64
+            ]
+        ];
+
+        /**
+         * Values of the RES main stat[stars][level]
+         */
+        const RES = [
+            "1" => [
+                "0" => 1,
+                "1" => 2,
+                "2" => 3,
+                "3" => 4,
+                "4" => 5,
+                "5" => 6,
+                "6" => 7,
+                "7" => 8,
+                "8" => 9,
+                "9" => 10,
+                "10" => 11,
+                "11" => 12,
+                "12" => 13,
+                "13" => 14,
+                "14" => 15,
+                "15" => 18
+            ],
+            "2" => [
+                "0" => 2,
+                "1" => 3,
+                "2" => 4,
+                "3" => 5,
+                "4" => 6,
+                "5" => 7,
+                "6" => 8,
+                "7" => 9,
+                "8" => 10,
+                "9" => 11,
+                "10" => 12,
+                "11" => 13,
+                "12" => 14,
+                "13" => 15,
+                "14" => 16,
+                "15" => 19
+            ],
+            "3" => [
+                "0" => 4,
+                "1" => 6,
+                "2" => 8,
+                "3" => 10,
+                "4" => 12,
+                "5" => 14,
+                "6" => 16,
+                "7" => 18,
+                "8" => 20,
+                "9" => 22,
+                "10" => 24,
+                "11" => 26,
+                "12" => 28,
+                "13" => 30,
+                "14" => 32,
+                "15" => 38
+            ],
+            "4" => [
+                "0" => 6,
+                "1" => 8,
+                "2" => 10,
+                "3" => 13,
+                "4" => 15,
+                "5" => 17,
+                "6" => 19,
+                "7" => 21,
+                "8" => 24,
+                "9" => 26,
+                "10" => 28,
+                "11" => 30,
+                "12" => 32,
+                "13" => 35,
+                "14" => 37,
+                "15" => 44
+            ],
+            "5" => [
+                "0" => 9,
+                "1" => 11,
+                "2" => 14,
+                "3" => 16,
+                "4" => 19,
+                "5" => 21,
+                "6" => 23,
+                "7" => 26,
+                "8" => 28,
+                "9" => 31,
+                "10" => 33,
+                "11" => 35,
+                "12" => 38,
+                "13" => 40,
+                "14" => 43,
+                "15" => 51
+            ],
+            "6" => [
+                "0" => 12,
+                "1" => 15,
+                "2" => 18,
+                "3" => 21,
+                "4" => 24,
+                "5" => 27,
+                "6" => 30,
+                "7" => 33,
+                "8" => 36,
+                "9" => 39,
+                "10" => 42,
+                "11" => 45,
+                "12" => 48,
+                "13" => 51,
+                "14" => 54,
+                "15" => 64
+            ]
+        ];
+    };
+
     /**
      * Artifact types.
      *

+ 7 - 3
application/Controller.php

@@ -87,6 +87,10 @@
                             require_once(__DIR__ . "/API/v2/API_Controller.php");
                             new API_Controller($params);
                             break;
+                        case "v3":
+                            require_once(__DIR__ . "/API/v3/API_Controller.php");
+                            new API_Controller($params);
+                            break;
                     }
                 }
                 return;
@@ -284,9 +288,9 @@
                         require_once(PATH::PAGE . "Report_Skillup_Page.php");
                         $page = new Report_Skillup_Page($pars[1]);
                     }
-                    elseif (strtoupper($pars[1]) == "RUNECRAFT"){
-                        require_once(PATH::PAGE . "Report_Runecraft_Page.php");
-                        $page = new Report_Runecraft_Page($pars[1]);
+                    elseif (strtoupper($pars[1]) == "RUNE_ENCHANTMENT"){
+                        require_once(PATH::PAGE . "Report_Rune_Enchantment_Page.php");
+                        $page = new Report_Rune_Enchantment_Page($pars[1]);
                     }
                 }
                 elseif (strtoupper($pars[0]) == "OPTIMIZER"){

+ 6 - 7
application/action/delete_team.php

@@ -22,17 +22,16 @@
     function action(){
 
         global $db;
-
         $player = filter_input(INPUT_POST, 'uid');
         $id = filter_input(INPUT_POST, 'id');
         $statement = $db->prepare('DELETE FROM team WHERE uid = :uid AND id = :id;');
-        $statement->bindValue(':uid', $player);
-        $statement->bindValue(':id', $id);
-        $res = $statement->execute();
+        $statement->bindValue(':uid', $player, SQLITE3_INTEGER);
+        $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+        $statement->execute();
         $statement = $db->prepare('DELETE FROM team_unit WHERE team = :id;');
-        $statement->bindValue(':uid', $player);
-        $statement->bindValue(':id', $id);
-        $res = $statement->execute();
+        $statement->bindValue(':uid', $player, SQLITE3_INTEGER);
+        $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+        $statement->execute();
         return 0;
     }
 ?>

+ 10 - 10
application/action/edit_profile.php

@@ -37,8 +37,8 @@
                 return -1;
             }
             $s = $db->prepare('UPDATE player SET mail = :mail WHERE uid = :uid;');
-            $s->bindValue(':uid', $player);
-            $s->bindValue(':mail', $mail);
+            $s->bindValue(':uid', $player, SQLITE3_INTEGER);
+            $s->bindValue(':mail', $mail, SQLITE3_TEXT);
             if(!$s->execute()){
                 return -2;
             }
@@ -48,17 +48,17 @@
             $pass = sha1(filter_input(INPUT_POST, 'pass'));
             $currentPass = sha1(filter_input(INPUT_POST, 'currentPass'));
             $s = $db->prepare('SELECT COUNT(uid) AS count FROM player WHERE uid = :uid AND password = :currentPass;');
-            $s->bindValue(':uid', $player);
-            $s->bindValue(':currentPass', $currentPass);
+            $s->bindValue(':uid', $player, SQLITE3_INTEGER);
+            $s->bindValue(':currentPass', $currentPass, SQLITE3_TEXT);
             $q = $s->execute();
             $r = $q->fetchArray(SQLITE3_ASSOC);
             if ($r["count"] != 1){
                 return -3;
             }
             $s = $db->prepare('UPDATE player SET password = :pass WHERE uid = :uid AND password = :currentPass;');
-            $s->bindValue(':uid', $player);
-            $s->bindValue(':pass', $mail);
-            $s->bindValue(':currentPass', $currentPass);
+            $s->bindValue(':uid', $player, SQLITE3_INTEGER);
+            $s->bindValue(':pass', $pass, SQLITE3_TEXT);
+            $s->bindValue(':currentPass', $currentPass, SQLITE3_TEXT);
             if(!$s->execute()){
                 return -4;
             }
@@ -68,12 +68,12 @@
             $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
             $charactersLength = 16;
             $api = '';
-            for ($i = 0; $i < $length; $i++) {
+            for ($i = 0; $i < $charactersLength; $i++) {
                 $api .= $characters[rand(0, $charactersLength - 1)];
             }
             $s = $db->prepare('UPDATE player SET api_key = :api WHERE uid = :uid;');
-            $s->bindValue(':uid', $player);
-            $s->bindValue(':api', $api);
+            $s->bindValue(':uid', $player, SQLITE3_INTEGER);
+            $s->bindValue(':api', $api, SQLITE3_TEXT);
             if(!$s->execute()){
                 return -5;
             }

+ 24 - 14
application/action/login.php

@@ -26,27 +26,37 @@
         global $db;
 
         if (!isset($_POST['uname'], $_POST['password'])){
-error_log("-1");
-            return -1;
+            header("Location: " . URL::BASE);
+            exit();
         }
         $uname = SQLite3::escapeString($_POST['uname']);
         if (strlen($uname) == 0){
-error_log("-2");
-            return -2;
+            header("Location: " . URL::BASE);
+            exit();
         }
         $password = SQLite3::escapeString($_POST['password']);
         if (strlen($password) == 0){
-error_log("-3");
-            return -3;
+            header("Location: " . URL::BASE);
+            exit();
         }
         $password = hash('sha256', $password);
-error_log("SELECT uid FROM player WHERE upper(name) = upper('$uname') AND password = '$password';");
-        $q = $db->query("SELECT uid FROM player;");
-        $r = $q->fetchArray(SQLITE3_ASSOC);
-error_log("RET ID: " . $r["uid"]);
+        $statement = $db->prepare("
+          SELECT uid 
+          FROM player 
+          WHERE
+            upper(name) = upper(:name) AND 
+            password = :password;
+        ");
+        $statement->bindValue(":name", $uname, SQLITE3_TEXT);
+        $statement->bindValue(":password", $password, SQLITE3_TEXT);
+        $statement->execute();
+        $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
         if (!$r){
-error_log("-4");
-            return -4;
+            error_log("REDIRECT TO:" . URL::BASE);
+            header("Location: " . URL::BASE);
+            exit();
+            die();
+            return 401;
         }
         $uid = $r["uid"];
         setcookie("uid", $uid, time() + 5 * 24 * 60 * 60); // 5 Days
@@ -54,9 +64,9 @@ error_log("-4");
         $_SESSION['session'] = true;
         $_SESSION['name'] = $uname;
         $_SESSION['uid'] = $uid;
-        header("Location: /$uid/");
+        header("Location: " . URL::BASE . "/$uid/");
+        exit();
         die();
-error_log("0");
         return 0;
     }
 ?>

+ 1 - 2
application/action/logout.php

@@ -18,8 +18,7 @@
      * @global int The user id.
      */
     function action(){
-        global $UID;
-        $UID = null;
+        $GLOBALS["UID"] = null;
         setcookie("uid", null, time() - 3600);
         session_regenerate_id();
         $_SESSION['session'] = false;

+ 17 - 10
application/action/new_team.php

@@ -172,15 +172,15 @@
         $r = $q->fetchArray(SQLITE3_ASSOC);
         $team_id = $r["id"];
         $statement = $db->prepare('INSERT INTO team (uid, id, name, description, area_type, area, stage, difficulty, score) VALUES (:uid, :id, :name, :description, :area_type, :area, :stage, :difficulty, :score);');
-        $statement->bindValue(':uid', $player);
-        $statement->bindValue(':id', $team_id);
-        $statement->bindValue(':name', $name);
-        $statement->bindValue(':description', $description);
-        $statement->bindValue(':area_type', $area_type);
-        $statement->bindValue(':area', $area);
-        $statement->bindValue(':stage', $stage);
-        $statement->bindValue(':difficulty', $difficulty);
-        $statement->bindValue(':score', $score);
+        $statement->bindValue(':uid', $player, SQLITE3_INTEGER);
+        $statement->bindValue(':id', $team_id, SQLITE3_INTEGER);
+        $statement->bindValue(':name', $name, SQLITE3_TEXT);
+        $statement->bindValue(':description', $description, SQLITE3_TEXT);
+        $statement->bindValue(':area_type', $area_type, SQLITE3_INTEGER);
+        $statement->bindValue(':area', $area, SQLITE3_INTEGER);
+        $statement->bindValue(':stage', $stage, SQLITE3_INTEGER);
+        $statement->bindValue(':difficulty', $difficulty, SQLITE3_INTEGER);
+        $statement->bindValue(':score', $score, SQLITE3_INTEGER);
         $res = $statement->execute();
         if (!$res){
             return -5;
@@ -196,7 +196,14 @@
             if ($j < $total_front){
                 $front = 1;
             }
-            $s = "INSERT INTO team_unit VALUES ($team_id, $i, $leader, $front);";
+            $statement = $db->prepare("
+              INSERT INTO team_unit (team, unit, leader, front)
+              VALUES (:team, :unit, :leader, :front);
+            ");
+            $statement->bindValue(':team', $team_id, SQLITE3_INTEGER);
+            $statement->bindValue(':unit', $i, SQLITE3_INTEGER);
+            $statement->bindValue(':leader', $leader, SQLITE3_INTEGER);
+            $statement->bindValue(':front', $front, SQLITE3_INTEGER);
             $db->query($s);
             $j ++;
         }

+ 1 - 4
application/action/optimize_get_options.php

@@ -21,12 +21,9 @@
      * @return int|string 0 on success, negative values on error. If the API
      * key has been updated, the new key.
      * @category Action
-     * @global resource Database connection.
      */
     function action(){
 
-        global $db;
-
         // Increase max execution time.
         set_time_limit(60);
         
@@ -109,7 +106,7 @@
                         break;
                 }
                 $stat_values = [
-                    $rune->main_stat_value,
+                    $main_value,
                     $rune->innate_stat_value,
                     $rune->substat_1_value + $rune->substat_1_craft,
                     $rune->substat_2_value + $rune->substat_2_craft,

+ 14 - 25
application/action/optimize_get_rune_list.php

@@ -26,8 +26,6 @@
     function action(){
 
         global $db;
-        
-        $HARD_LIMIT = 100;
 
         // Increase max execution time.
         set_time_limit(60);
@@ -40,8 +38,6 @@
             5 => [],
             6 => []
         ];
-        
-        $options = [];
 
         $response = null;
 
@@ -84,19 +80,6 @@
         if ($tuning == null){
             return -6;
         }
-        
-        $min = [
-            "attack" => intval(filter_input(INPUT_POST, 'min_attack')),
-            "defense" => intval(filter_input(INPUT_POST, 'min_defense')),
-            "hp" => intval(filter_input(INPUT_POST, 'min_hp')),
-            "speed" => intval(filter_input(INPUT_POST, 'min_speed')),
-            "crit_rate" => intval(filter_input(INPUT_POST, 'min_crit_rate')),
-            "crit_damage" => intval(filter_input(INPUT_POST, 'min_crit_damage')),
-            "accuracy" => intval(filter_input(INPUT_POST, 'min_accuracy')),
-            "resistance" => intval(filter_input(INPUT_POST, 'min_resistance')),
-            "ehp" => intval(filter_input(INPUT_POST, 'min_ehp')),
-            "dmg" => intval(filter_input(INPUT_POST, 'min_dmg'))
-        ];
 
         // Instantiate the unit
         $unit = new Unit($unit_id, true, $uid);
@@ -123,7 +106,7 @@
             substat_4_value + substat_4_grind AS substat_4_value
           FROM rune
           WHERE
-            uid = $uid AND
+            uid = :uid AND
             type IN (
         ";
         foreach ($sets as $set){
@@ -134,14 +117,14 @@
             case 0: // Storage only (or itself)
                 $base_s .= "
                   (
-                    assigned_to = $unit_id OR
+                    assigned_to = :unit_id OR
                     assigned_to IS NULL
                   )";
                 break;
             case 1: // Units in no teams (or itself)
                 $base_s .= "
                   (
-                    assigned_to = $unit_id OR
+                    assigned_to = :unit_id OR
                     assigned_to IS NULL OR 
                     assigned_to NOT IN (SELECT DISTINCT unit FROM team_unit)
                   )
@@ -150,7 +133,7 @@
             case 2: // Units in teams with 0 score (or itself)
                 $base_s .= "
                   (
-                    assigned_to = $unit_id OR
+                    assigned_to = :unit_id OR
                     assigned_to IS NULL OR 
                     assigned_to NOT IN (SELECT DISTINCT unit FROM team_unit) OR
                     assigned_to NOT IN (
@@ -175,7 +158,7 @@
                 $base_s .= " 1 = 0 ";
         }
         $s_order = " ORDER BY stars DESC, original_quality DESC, max_efficiency DESC, efficiency DESC, level DESC";
-        $s_in = " main_stat IN (" . $stats[0] . ", " . $stats[1] . ", " . $stats[2] . ") ";
+        $s_in = " main_stat IN (:stat_0, :stat_1, :stat_2) ";
 
         $s = ["", "", "", "", "", "", ""];
         $s[1] = $base_s . " AND slot = 1 " . $s_order;
@@ -187,7 +170,13 @@
 
         $total_candidates = 0;
         for ($i = 1; $i <= 6; $i ++){
-            $q = $db->query($s[$i]);
+            $statement = $db->prepare($s[$i]);
+            $statement->bindValue(':uid', $uid, SQLITE3_INTEGER);
+            $statement->bindValue(':unit_id', $unit_id, SQLITE3_INTEGER);
+            $statement->bindValue(':stat_0', $stats[0], SQLITE3_INTEGER);
+            $statement->bindValue(':stat_1', $stats[1], SQLITE3_INTEGER);
+            $statement->bindValue(':stat_2', $stats[2], SQLITE3_INTEGER);
+            $q = $statement->execute();
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 $rune = create_rune_array($unit, $r, $tuning);
                 //array_push($candidates[$i], new Rune($r["id"]));
@@ -195,7 +184,7 @@
                 $total_candidates ++;
             }
         }
-        $max_combinations = sizeof($candidates[1]) * sizeof($candidates[2]) * sizeof($candidates[3]) * sizeof($candidates[4]) * sizeof($candidates[5]) * sizeof($candidates[6]);
+        //$max_combinations = sizeof($candidates[1]) * sizeof($candidates[2]) * sizeof($candidates[3]) * sizeof($candidates[4]) * sizeof($candidates[5]) * sizeof($candidates[6]);
         
         // TODO: Build response
         $response = json_encode($candidates);
@@ -211,7 +200,7 @@
      * Creates an array with the stats provided by a rune on a unit.
      *
      * @param Unit $unit Target unit.
-     * @param SQLResult $r Rune main stat ID.
+     * @param int $r Rune main stat ID.
      * @param int $stars Rune stars.
      * @return int Value of the main stat at the selected level.
      */

+ 14 - 8
application/action/rate_team.php

@@ -23,14 +23,20 @@
      */
     function action(){
         global $db;
-        $player = filter_input(INPUT_GET, 'uid');
-        $team = filter_input(INPUT_GET, 'team');
-        $score = intval(filter_input(INPUT_GET, 'score'));
-        $statement = $db->prepare('UPDATE team SET score = :score WHERE uid = :uid AND id = :team;');
-        $statement->bindValue(':score', $score);
-        $statement->bindValue(':uid', $player);
-        $statement->bindValue(':team', $team);
-        $res = $statement->execute();
+        $player = filter_input(INPUT_GET, "uid");
+        $team = filter_input(INPUT_GET, "team");
+        $score = intval(filter_input(INPUT_GET, "score"));
+        $statement = $db->prepare("
+          UPDATE team
+          SET score = :score
+          WHERE
+            uid = :uid AND
+            id = :team;
+        ");
+        $statement->bindValue(":score", $score, SQLITE3_INTEGER);
+        $statement->bindValue(":uid", $player, SQLITE3_INTEGER);
+        $statement->bindValue(":team", $team, SQLITE3_INTEGER);
+        $statement->execute();
         return 0;
     }
 ?>

+ 34 - 14
application/action/save_run_team.php

@@ -34,22 +34,37 @@
         $q = $db->query($s);
         $r = $q->fetchArray(SQLITE3_ASSOC);
         $team_id = $r["id"];
-        $s = "SELECT run.area AS area, run.stage AS stage, run.difficulty AS difficulty, k_area.type AS area_type FROM run, k_area WHERE run.uid = '$player' AND run.id = '$run' AND k_area.id = run.area AND run.helper = 0;";
-        $q = $db->query($s);
-        $r = $q->fetchArray(SQLITE3_ASSOC);
+        $statement = $db->prepare("
+          SELECT
+            run.area AS area,
+            run.stage AS stage,
+            run.difficulty AS difficulty,
+            k_area.type AS area_type
+          FROM
+            run,
+            k_area
+          WHERE
+            run.uid = :uid AND
+            run.id = :run AND
+            k_area.id = run.area AND
+            run.helper = 0;
+        ");
+        $statement->bindValue(':uid', $player, SQLITE3_INTEGER);
+        $statement->bindValue(':run', $run, SQLITE3_INTEGER);
+        $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
         if (!$r){
             return -2;
         }
         $statement = $db->prepare('INSERT INTO team (uid, id, name, description, area_type, area, stage, difficulty, score) VALUES (:uid, :id, :name, :description, :area_type, :area, :stage, :difficulty, :score);');
-        $statement->bindValue(':uid', $player);
-        $statement->bindValue(':id', $team_id);
-        $statement->bindValue(':name', $name);
-        $statement->bindValue(':description', $description);
-        $statement->bindValue(':area_type', $r["area_type"]);
-        $statement->bindValue(':area', $r["area"]);
-        $statement->bindValue(':stage', $r["stage"]);
-        $statement->bindValue(':difficulty', $r["difficulty"]);
-        $statement->bindValue(':score', $score);
+        $statement->bindValue(':uid', $player, SQLITE3_INTEGER);
+        $statement->bindValue(':id', $team_id, SQLITE3_INTEGER);
+        $statement->bindValue(':name', $name, SQLITE3_TEXT);
+        $statement->bindValue(':description', $description, SQLITE3_TEXT);
+        $statement->bindValue(':area_type', $r["area_type"], SQLITE3_INTEGER);
+        $statement->bindValue(':area', $r["area"], SQLITE3_INTEGER);
+        $statement->bindValue(':stage', $r["stage"], SQLITE3_INTEGER);
+        $statement->bindValue(':difficulty', $r["difficulty"], SQLITE3_INTEGER);
+        $statement->bindValue(':score', 0, SQLITE3_INTEGER);
         $res = $statement->execute();
         if (!$res){
             return -3;
@@ -57,8 +72,13 @@
         $s = "SELECT unit FROM run_party WHERE run = $run AND unit IS NOT NULL;";
         $q = $db->query($s);
         while ($r = $q->fetchArray(SQLITE3_ASSOC)){
-            $s = "INSERT INTO team_unit VALUES ('$team_id', '" . $r["unit"] . "');";
-            $db->query($s);
+            $statement = $db->prepare("
+              INSERT INTO team_unit (team, unit)
+              VALUES (:team, :unit)
+            ");
+            $statement->bindValue(':team', $team_id, SQLITE3_INTEGER);
+            $statement->bindValue(':unit', $r["unit"], SQLITE3_INTEGER);
+            $statement->execute();
         }
         return 0;
     }

+ 45 - 51
application/entity/Artifact.php

@@ -65,14 +65,14 @@
         /**
          * @var int Artifact quality.
          *
-         * @see QUALITY
+         * @see QUALITY_ID
          */
         public $quality;
 
         /**
          * @var int Artifact original quality.
          *
-         * @see QUALITY
+         * @see QUALITY_ID
          */
         public $original_quality;
 
@@ -201,41 +201,42 @@
 
             global $db;
 
-            $s =
-              "SELECT " .
-              "  id, " .
-              "  assigned_to, " .
-              "  type, " .
-              "  attribute, " .
-              "  archetype, " .
-              "  level, " .
-              "  quality, " .
-              "  original_quality, " .
-              "  value, " .
-              "  efficiency, " .
-              "  max_efficiency, " .
-              "  main_stat, " .
-              "  main_stat_value, " .
-              "  effect_1, " .
-              "  effect_1_value, " .
-              "  effect_1_enchant, " .
-              "  effect_1_grind, " .
-              "  effect_2, " .
-              "  effect_2_value, " .
-              "  effect_2_enchant, " .
-              "  effect_2_grind, " .
-              "  effect_3, " .
-              "  effect_3_value, " .
-              "  effect_3_enchant, " .
-              "  effect_3_grind, " .
-              "  effect_4, " .
-              "  effect_4_value, " .
-              "  effect_4_enchant, " .
-              "  effect_4_grind " .
-              "FROM artifact " .
-              "WHERE id = '$id'; ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
+            $statement = $db->prepare("
+              SELECT
+                id,
+                assigned_to,
+                type,
+                attribute,
+                archetype,
+                level,
+                quality,
+                original_quality,
+                value,
+                efficiency,
+                max_efficiency,
+                main_stat,
+                main_stat_value,
+                effect_1,
+                effect_1_value,
+                effect_1_enchant,
+                effect_1_grind,
+                effect_2,
+                effect_2_value,
+                effect_2_enchant,
+                effect_2_grind,
+                effect_3,
+                effect_3_value,
+                effect_3_enchant,
+                effect_3_grind,
+                effect_4,
+                effect_4_value,
+                effect_4_enchant,
+                effect_4_grind
+              FROM artifact
+              WHERE id = :id;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
             if ($r){
                 $this->id = $r["id"];
                 $this->assigned_to = $r["assigned_to"];
@@ -252,20 +253,12 @@
                 $this->main_stat_value = $r["main_stat_value"];
                 $this->effect_1 = $r["effect_1"];
                 $this->effect_1_value = $r["effect_1_value"];
-                //$this->effect_1_enchant = $r["effect_1_enchant"];
-                //$this->effect_1_grind = $r["effect_1_grind"];
                 $this->effect_2 = $r["effect_2"];
                 $this->effect_2_value = $r["effect_2_value"];
-                //$this->effect_2_enchant = $r["effect_2_enchant"];
-                //$this->effect_2_grind = $r["effect_2_grind"];
                 $this->effect_3 = $r["effect_3"];
                 $this->effect_3_value = $r["effect_3_value"];
-                //$this->effect_3_enchant = $r["effect_3_enchant"];
-                //$this->effect_3_grind = $r["effect_3_grind"];
                 $this->effect_4 = $r["effect_4"];
                 $this->effect_4_value = $r["effect_4_value"];
-                //$this->effect_4_enchant = $r["effect_4_enchant"];
-                //$this->effect_4_grind = $r["effect_4_grind"];
                 $this->refresh_names();
             }
         }
@@ -314,12 +307,13 @@
 
             global $db;
 
-            $s =
-              "SELECT name " .
-              "FROM k_artifact_effect " .
-              "WHERE id = '$effect'; ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
+            $statement = $db->prepare("
+              SELECT name
+              FROM k_artifact_effect
+              WHERE id = :id;
+            ");
+            $statement->bindValue(':id', $effect, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
             if ($r){
                 return str_replace("{}", $value, $r["name"]);
             }

+ 15 - 13
application/entity/Building.php

@@ -51,19 +51,21 @@
 
             global $db;
 
-            $s = "
-                SELECT
-                  uid,
-                  building,
-                  gain
-                FROM building
-                WHERE id = $id;
-            ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
-            $this->uid = $r["uid"];
-            $this->building = new K_Building($r["building"]);
-            $this->gain = $r["gain"];
+            $statement = $db->prepare("
+              SELECT
+                uid,
+                building,
+                gain
+              FROM building
+              WHERE id = :id;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
+            if ($r){
+                $this->uid = $r["uid"];
+                $this->building = new K_Building($r["building"]);
+                $this->gain = $r["gain"];
+            }
         }
     }
 ?>

+ 15 - 13
application/entity/Decoration.php

@@ -50,19 +50,21 @@
 
             global $db;
 
-            $s = "
-                SELECT
-                  uid,
-                  decoration,
-                  level
-                FROM decoration
-                WHERE id = $id;
-            ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
-            $this->uid = $r["uid"];
-            $this->decoration = new K_Decoration($r["decoration"]);
-            $this->level = $r["level"];
+            $statement = $db->prepare("
+              SELECT
+                uid,
+                decoration,
+                level
+              FROM decoration
+              WHERE id = :id;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
+            if ($r){
+                $this->uid = $r["uid"];
+                $this->decoration = new K_Decoration($r["decoration"]);
+                $this->level = $r["level"];
+            }
         }
     }
 ?>

+ 47 - 32
application/entity/Rune_Craft.php → application/entity/Enchantment.php

@@ -1,6 +1,6 @@
 <?php
     /**
-     * Rune craft items entity file.
+     * Enchantments items entity file.
      *
      * Creates the entity and makes it available.
      *
@@ -14,13 +14,13 @@
     require_once(PATH::ENTITY . "K_Rune_Set.php");
 
     /**
-     * Owned rune craft item.
+     * Owned enchantments.
      *
-     * Represents an object from the table 'rune_craft'.
+     * Represents an object from the table 'enchantment'.
      *
      * @category Entity
      */
-    class Rune_Craft extends Entity{
+    class Enchantment extends Entity{
 
         /**
          * @var int Owner identifier.
@@ -35,7 +35,7 @@
         /**
          * @var int Item quality.
          *
-         * @see QUALITY
+         * @see QUALITY_ID
          */
         public $quality;
 
@@ -52,7 +52,7 @@
         /**
          * @var int Stat the item modifies.
          *
-         * @see RUNE_STAT
+         * @see RUNE_STAT_ID
          */
         public $stat;
 
@@ -114,35 +114,35 @@
 
             global $db;
 
-            $s = "
+            $statement = $db->prepare("
               SELECT
-                rune_craft.id AS id,
-                rune_craft.quality As quality,
-                rune_craft.rune AS rune,
-                rune_craft.stat AS stat,
-                rune_craft.value AS value,
-                rune_craft.amount AS amount,
-                k_rune_craft_type.name AS name,
-                k_rune_craft_type.gem AS gem,
-                k_rune_craft_type.inmemorial AS inmemorial,
-                k_rune_craft_type.ancient AS ancient,
-                k_rune_craft_range.min AS min,
-                k_rune_craft_range.max AS max
+                enchantment.id AS id,
+                enchantment.quality As quality,
+                enchantment.rune AS rune,
+                enchantment.stat AS stat,
+                enchantment.value AS value,
+                enchantment.amount AS amount,
+                k_enchantment_type.name AS name,
+                k_enchantment_type.gem AS gem,
+                k_enchantment_type.inmemorial AS inmemorial,
+                k_enchantment_type.ancient AS ancient,
+                k_enchantment_range.min AS min,
+                k_enchantment_range.max AS max
               FROM
-                rune_craft,
-                k_rune_craft_type,
-                k_rune_craft_range
+                enchantment,
+                k_enchantment_type,
+                k_enchantment_range
               WHERE
-                rune_craft.id = $id AND
-                rune_craft.type = k_rune_craft_type.id AND
-                k_rune_craft_type.gem = k_rune_craft_range.gem AND
-                k_rune_craft_type.gem = k_rune_craft_range.gem AND
-                k_rune_craft_type.ancient = k_rune_craft_range.ancient AND
-                rune_craft.quality = k_rune_craft_range.quality AND
-                rune_craft.stat = k_rune_craft_range.stat;
-            ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
+                enchantment.id = :id AND
+                enchantment.type = k_enchantment_type.id AND
+                k_enchantment_type.gem = k_enchantment_range.gem AND
+                k_enchantment_type.gem = k_enchantment_range.gem AND
+                k_enchantment_type.ancient = k_enchantment_range.ancient AND
+                enchantment.quality = k_enchantment_range.quality AND
+                enchantment.stat = k_enchantment_range.stat;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
             if ($r){
                 $this->id = $r["id"];
                 $this->gem = $r["gem"];
@@ -224,5 +224,20 @@
             }
         }
 
+        /**
+         * Calculates name variables.
+         */
+        public function refresh_names(){
+            $this->stat_name = $this->stat_name($this->stat);
+            $this->quality_name = $this->quality_name($this->quality);
+            $this->name = $this->rune->name . " " . $this->stat;
+            if ($this->inmemorial == 0){
+                $this->name = $this->rune->name . " " . $this->stat;
+            }
+            else{
+                $this->name = "Inmemorial " . $this->stat;
+            }
+        }
+
     }
 ?>

+ 43 - 38
application/entity/Fusion.php

@@ -40,21 +40,23 @@
         public function __construct($id, $uid){
             global $db;
             parent::__construct($id, true);
-            $s = "
+            $statement = $db->prepare("
               SELECT unit.id AS id
               FROM
                 unit,
                 k_unit
               WHERE
                 k_unit.id = unit.unit AND
-                unit.uid = '$uid' AND
+                unit.uid = :uid AND
                 (
-                  k_unit.id = $id OR
-                  k_unit.awakens_from = $id OR
-                  k_unit.awakens_to = $id
+                  k_unit.id = :id OR
+                  k_unit.awakens_from = :id OR
+                  k_unit.awakens_to = :id
                 );
-            ";
-            $q = $db->query($s);
+            ");
+            $statement->bindValue(':uid', $uid, SQLITE3_INTEGER);
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $q = $statement->execute();
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 array_push($this->owned, new Unit($r["id"], false));
             }
@@ -125,7 +127,7 @@
 
             global $db;
 
-            $s = "
+            $statement = $db->prepare("
               SELECT DISTINCT
                 product,
                 stars,
@@ -135,18 +137,18 @@
                 product = $id OR
                 product = (SELECT awakens_to FROM k_unit WHERE id = $id) OR
                 product = (SELECT awakens_from FROM k_unit WHERE id = $id);
-            ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
             $this->product = new Monster_Fusion($r["product"], $uid);
             $this->product_awaken = new Monster_Fusion($this->product->awakens_to, $uid);
             $this->stars = $r["stars"];
             $this->cost = $r["cost"];
-            $s_f = "
+            $statement = $db->prepare("
               SELECT DISTINCT ingredient
               FROM k_fusion
               WHERE
-                product = " . $this->product->id . " AND
+                product = :product AND
                 ingredient IN (
                   SELECT DISTINCT k_unit.id
                   FROM
@@ -157,34 +159,37 @@
                     k_fusion.product = k_unit.awakens_to OR
                     k_fusion.product = k_unit.id
                 );
-            ";
-            $q_f = $db->query($s_f);
+            ");
+            $statement->bindValue(':product', $this->product->id, SQLITE3_INTEGER);
+            $q_f = $statement->execute();
             while ($r_f = $q_f->fetchArray(SQLITE3_ASSOC)){
                 array_push($this->fusion, new Fusion($r_f["ingredient"], $uid));
             }
-            $s_m =
-              "SELECT  " .
-              "  id, " .
-              "  awakens_from " .
-              "FROM k_unit " .
-              "WHERE id IN ( " .
-              "  SELECT DISTINCT " .
-              "    ingredient " .
-              "  FROM k_fusion " .
-              "  WHERE  " .
-              "    product = " . $this->product->id . " AND " .
-              "    ingredient NOT IN (  " .
-              "      SELECT DISTINCT k_unit.id  " .
-              "      FROM  " .
-              "        k_fusion,  " .
-              "        k_unit  " .
-              "      WHERE " .
-              "        k_fusion.product = k_unit.awakens_from OR " .
-              "        k_fusion.product = k_unit.awakens_to OR " .
-              "        k_fusion.product = k_unit.id " .
-              "    ) " .
-              "  ); ";
-            $q_m = $db->query($s_m);
+            $statement = $db->prepare("
+              SELECT 
+                id,
+                awakens_from
+              FROM k_unit
+              WHERE id IN (
+                SELECT DISTINCT
+                  ingredient
+                FROM k_fusion
+                WHERE 
+                  product = :product AND
+                  ingredient NOT IN ( 
+                    SELECT DISTINCT k_unit.id 
+                    FROM 
+                      k_fusion, 
+                      k_unit 
+                    WHERE
+                      k_fusion.product = k_unit.awakens_from OR
+                      k_fusion.product = k_unit.awakens_to OR
+                      k_fusion.product = k_unit.id
+                  )
+                );
+            ");
+            $statement->bindValue(':product', $this->product->id, SQLITE3_INTEGER);
+            $q_m = $statement->execute();
             while ($r_m = $q_m->fetchArray(SQLITE3_ASSOC)){
                 $m = new Monster_Fusion($r_m["id"], $uid);
                 array_push($this->ingredient, $m);

+ 29 - 25
application/entity/Guild.php

@@ -85,21 +85,23 @@
 
             global $db;
 
-            $s =
-              "SELECT " .
-              "  id, " .
-              "  name, " .
-              "  name, " .
-              "  level, " .
-              "              experience, " .
-              "  recruiting, " .
-              "  members, " .
-              "  leader, " .
-              "  comment, " .
-              "  notice " .
-              "FROM guild " .
-              "WHERE id = $id; ";
-            $q = $db->query($s);
+            $statement = $db->prepare("
+              SELECT
+                id,
+                name,
+                name,
+                level,
+                experience,
+                recruiting,
+                members,
+                leader,
+                comment,
+                notice
+              FROM guild
+              WHERE id = :id;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $q = $statement->execute();
             $r = $q->fetchArray(SQLITE3_ASSOC);
             if ($r){
                 $this->id = $r["id"];
@@ -112,16 +114,18 @@
                 $this->comment = HTML::e($r["comment"]);
                 $this->notice = HTML::e($r["notice"]);
             }
-            $s =
-              "SELECT id " .
-              "FROM guild_member " .
-              "WHERE guild = $this->id " .
-              "ORDER BY " .
-              "  grade = 1 DESC, " .
-              "  grade = 3 DESC, " .
-              "  grade = 2 DESC, " .
-              "  arena_score DESC; ";
-            $q = $db->query($s);
+            $statement = $db->prepare("
+              SELECT id
+              FROM guild_member
+              WHERE guild = :guild
+              ORDER BY
+                grade = 1 DESC,
+                grade = 3 DESC,
+                grade = 2 DESC,
+                arena_score DESC;
+            ");
+            $statement->bindValue(':guild', $this->id, SQLITE3_INTEGER);
+            $q = $statement->execute();
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 array_push($this->members, new Guild_Member($r["id"]));
             }

+ 18 - 17
application/entity/Guild_Member.php

@@ -90,23 +90,24 @@
 
             global $db;
 
-            $s =
-              "SELECT " .
-              "  id, " .
-              "  guild, " .
-              "  name, " .
-              "  level, " .
-              "  grade, " .
-              "  rating, " .
-              "  arena_score, " .
-              "  joined, " .
-              "  last_login, " .
-              "  in_war, " .
-              "  has_defense " .
-              "FROM guild_member " .
-              "WHERE id = $id; ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
+            $statement = $db->prepare("
+              SELECT
+                id,
+                guild,
+                name,
+                level,
+                grade,
+                rating,
+                arena_score,
+                joined,
+                last_login,
+                in_war,
+                has_defense
+              FROM guild_member
+              WHERE id = :id;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
             if ($r){
                 $this->id = $r["id"];
                 $this->guild = $r["guild"];

+ 21 - 17
application/entity/Inventory.php

@@ -82,26 +82,30 @@
                   inventory.type = k_inventory_type.id AND
                   inventory.id = k_inventory.id AND
                   inventory.type = k_inventory.type AND
-                  inventory.id = $id
+                  inventory.id = :id
             ";
             if ($type != null){
-                $s .= "AND inventory.type = $type";
+                $s .= "AND inventory.type = :type";
             }
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
-            $this->type = $r["type"];
-            $this->amount = $r["amount"];
-            if ($r["type"] == INVENTORY_TYPE_ID::GUILD_MONSTER_PIECE || $r["type"] == INVENTORY_TYPE_ID::MONSTER_PIECE){
-                $this->is_monster_pices = true;
-                $this->id = new K_Unit($r["id"]);
-                $this->name = HTML::e($this->id->title) . " Pieces";
-                $this->description = HTML::e($this->id->title) . " Pieces";
-            }
-            else{
-                $this->is_monster_pices = false;
-                $this->id = $r["id"];
-                $this->name = HTML::e($r["name"]);
-                $this->description = HTML::e($r["description"]);
+            $statement = $db->prepare($s);
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $statement->bindValue(':type', $type, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
+            if ($r){
+                $this->type = $r["type"];
+                $this->amount = $r["amount"];
+                if ($r["type"] == INVENTORY_TYPE_ID::GUILD_MONSTER_PIECE || $r["type"] == INVENTORY_TYPE_ID::MONSTER_PIECE){
+                    $this->is_monster_pices = true;
+                    $this->id = new K_Unit($r["id"]);
+                    $this->name = HTML::e($this->id->title) . " Pieces";
+                    $this->description = HTML::e($this->id->title) . " Pieces";
+                }
+                else{
+                    $this->is_monster_pices = false;
+                    $this->id = $r["id"];
+                    $this->name = HTML::e($r["name"]);
+                    $this->description = HTML::e($r["description"]);
+                }
             }
         }
 

+ 28 - 11
application/entity/K_Area.php

@@ -53,19 +53,20 @@
 
             $this->id = $id;
             $this->type = $type;
-            $s = "
+            $statement = $db->prepare("
                 SELECT
                   id,
                   type,
                   name
                 FROM k_area
                 WHERE
-                  id = '$id' AND
-                  type = '$type' 
-                ORDER BY type = '$type' DESC;
-            ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
+                  id = :id AND
+                  type = :type
+                ORDER BY type DESC;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $statement->bindValue(':type', $type, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
             if ($r){
                 $this->name = HTML::e($r["name"]);
             }
@@ -73,6 +74,17 @@
                 $this->type = AREA_TYPE_ID::CAIROS_DUNGEON;
                 $this->name = "Hall of Heroes";
             }
+            else{
+                $statement = $db->prepare("
+                  SELECT name
+                  FROM k_area_type
+                  WHERE id = :type;
+                ");
+                $statement->bindValue(':type', $type, SQLITE3_INTEGER);
+                if ($r){
+                    $this->name = HTML::e($r["name"]);
+                }
+            }
         }
 
         /**
@@ -88,9 +100,17 @@
                 // Hall of Heroes
                 return APPLICATION::img("AREA", "HOH");
             }
+            elseif ($this->type == AREA_TYPE_ID::RIFT_RAID){
+                return APPLICATION::img("UNIT", 44007);
+            }
+            elseif ($this->type == AREA_TYPE_ID::ARENA){
+                return APPLICATION::img("AREA", "ARENA");
+            }
+            elseif ($this->type == AREA_TYPE_ID::DIMENSIONAL_RIFT){
+                return APPLICATION::img("CURRENCY", "darkportalenergy");
+            }
             elseif (
                 $this->type == AREA_TYPE_ID::SCENARIO ||
-                $this->type == AREA_TYPE_ID::ARENA ||
                 $this->type == AREA_TYPE_ID::GUILD_WAR ||
                 $this->type == AREA_TYPE_ID::GUILD_SIEGE ||
                 $this->type == AREA_TYPE_ID::WORLD_BOSS ||
@@ -159,9 +179,6 @@
                         break;
                 }
             }
-            elseif ($this->type == AREA_TYPE_ID::RIFT_RAID){
-                return APPLICATION::img("UNIT", 44007);
-            }
             elseif ($this->type == AREA_TYPE_ID::DIMENSIONAL_HOLE){
                 switch ($this->id){
                     case DUNGEON_ID::FOREST_OF_ROARING_BEASTS:

+ 10 - 9
application/entity/K_Building.php

@@ -50,15 +50,16 @@
 
             global $db;
 
-            $s =
-              "SELECT " .
-              "  id, " .
-              "  name, " .
-              "  description " .
-              "FROM k_building " .
-              "WHERE id = '$id'; ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
+            $statement = $db->prepare("
+              SELECT
+                id,
+                name,
+                description
+              FROM k_building
+               WHERE id = :id;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
             if ($r){
                 $this->id = $r["id"];
                 $this->name = HTML::e($r["name"]);

+ 10 - 9
application/entity/K_Decoration.php

@@ -80,7 +80,7 @@
 
             global $db;
 
-            $s = "
+            $statement = $db->prepare("
                 SELECT
                   id,
                   name,
@@ -90,10 +90,10 @@
                   element,
                   max_level
                 FROM k_decoration
-                WHERE id = $id;
-            ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
+               WHERE id = :id;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
             if ($r){
                 $this->id = $r["id"];
                 $this->name = HTML::e($r["name"]);
@@ -102,15 +102,16 @@
                 $this->stat = $r["affected_stat"];
                 $this->element = $r["element"];
                 $this->max_level = $r["max_level"];
-                $s = "
+                $statement = $db->prepare("
                     SELECT
                       level,
                       bonus,
                       cost
                     FROM k_decoration_level
-                    WHERE decoration = $id
-                ";
-                $q = $db->query($s);
+                    WHERE decoration = :id
+                ");
+                $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+                $q = $statement->execute();
                 while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                     $this->level_bonus[$r["level"]] = $r["bonus"];
                     $this->level_cost[$r["level"]] = $r["cost"];

+ 11 - 10
application/entity/K_Effect.php

@@ -54,16 +54,17 @@
 
             global $db;
 
-            $s =
-              "SELECT " .
-              "  id, " .
-              "  name, " .
-              "  is_buff, " .
-              "  description " .
-              "FROM k_effect " .
-              "WHERE id = $id;";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
+            $statement = $db->prepare("
+              SELECT
+                id,
+                name,
+                is_buff,
+                description
+              FROM k_effect
+              WHERE id = :id;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
             if ($r){
                 $this->id = $r["id"];
                 $this->name = HTML::e($r["name"]);

+ 11 - 10
application/entity/K_Guild_Skill.php

@@ -54,16 +54,17 @@
 
             global $db;
 
-            $s =
-              "SELECT " .
-              "  id, " .
-              "  skill_group, " .
-              "  level, " .
-              "  effect " .
-              "FROM k_guild_skill " .
-              "WHERE id = $id; ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
+            $statement = $db->prepare("
+              SELECT
+                id,
+                skill_group,
+                level,
+                effect
+              FROM k_guild_skill
+              WHERE id = :id;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
             if ($r){
                 $this->id = $r["id"];
                 $this->group = $r["skill_group"];

+ 20 - 17
application/entity/K_Guild_Skill_Group.php

@@ -55,27 +55,30 @@
 
             global $db;
 
-            $s =
-              "SELECT " .
-              "  id, " .
-              "  name, " .
-              "  description " .
-              "FROM k_guild_skill_group " .
-              "WHERE id = $id; ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
+            $statement = $db->prepare("
+              SELECT
+                id,
+                name,
+                description
+              FROM k_guild_skill_group
+              WHERE id = :id;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
             if ($r){
                 $this->id = $r["id"];
                 $this->name = HTML::e($r["name"]);
                 $this->description = HTML::e($r["description"]);
-            }
-            $s =
-              "SELECT id " .
-              "FROM k_guild_skill " .
-              "WHERE skill_group = $this->id; ";
-            $q = $db->query($s);
-            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
-                array_push($this->skills, new K_Guild_Skill($r["id"]));
+                $statement = $db->prepare("
+                  SELECT id
+                  FROM k_guild_skill
+                  WHERE skill_group = :id;
+                ");
+                $statement->bindValue(':id', $this->id, SQLITE3_INTEGER);
+                $q = $statement->execute();
+                while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                    array_push($this->skills, new K_Guild_Skill($r["id"]));
+                }
             }
         }
 

+ 22 - 18
application/entity/K_Inventory.php

@@ -54,27 +54,31 @@
         public function __construct($id, $type = null){
             global $db;
             $s = "
-                SELECT
-                  k_inventory.id AS id,
-                  k_inventory.name AS name,
-                  k_inventory.description AS description,
-                  k_inventory_type.name AS type
-                FROM
-                  k_inventory,
-                  k_inventory_type
-                WHERE
-                  k_inventory.type = k_inventory_type.id AND
-                  k_inventory.id = $id
+              SELECT
+                k_inventory.id AS id,
+                k_inventory.name AS name,
+                k_inventory.description AS description,
+                k_inventory_type.name AS type
+              FROM
+                k_inventory,
+                k_inventory_type
+              WHERE
+                k_inventory.type = k_inventory_type.id AND
+                k_inventory.id = :id
             ";
             if ($type != null){
-                $s .= "AND k_inventory.type = $type";
+                $s .= "AND k_inventory.type = :type";
+            }
+            $statement = $db->prepare($s);
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $statement->bindValue(':type', $type, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
+            if ($r){
+                $this->type = $r["type"];
+                $this->id = $r["id"];
+                $this->name = HTML::e($r["name"]);
+                $this->description = HTML::e($r["description"]);
             }
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
-            $this->type = $r["type"];
-            $this->id = $r["id"];
-            $this->name = HTML::e($r["name"]);
-            $this->description = HTML::e($r["description"]);
         }
 
         /**

+ 20 - 17
application/entity/K_Leader_Skill.php

@@ -59,22 +59,25 @@
 
             global $db;
 
-            $s =
-              "SELECT " .
-              "  id, " .
-              "  stat, " .
-              "  amount, " .
-              "  area, " .
-              "  element " .
-              "FROM k_leader_skill " .
-              "WHERE id = $id; ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
-            $this->id = $r["id"];
-            $this->attribute = $r["stat"];
-            $this->amount = $r["amount"];
-            $this->area = $r["area"];
-            $this->element = $r["element"]; 
+            $statement = $db->prepare("
+              SELECT
+                id,
+                stat,
+                amount,
+                area,
+                element
+              FROM k_leader_skill
+             WHERE id = :id;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
+            if ($r){
+                $this->id = $r["id"];
+                $this->attribute = $r["stat"];
+                $this->amount = $r["amount"];
+                $this->area = $r["area"];
+                $this->element = $r["element"];
+            }
         }
 
         /**
@@ -151,7 +154,7 @@
                     $s = $s . " in Guild content";
                     break;
             }
-            $s = $s . ".";
+            $s = str_replace("  ", " ", $s) . ".";
             return $s;
         }
 

+ 11 - 10
application/entity/K_Rune_Set.php

@@ -54,16 +54,17 @@
 
             global $db;
 
-            $s =
-              "SELECT " .
-              "  id, " .
-              "  name, " .
-              "  amount, " .
-              "  description " .
-              "FROM k_rune_set " .
-              "WHERE id = $id;";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
+            $statement = $db->prepare("
+              SELECT
+                id,
+                name,
+                amount,
+                description
+              FROM k_rune_set
+              WHERE id = :id;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
             if ($r){
                 $this->id = $r["id"];
                 $this->name = HTML::e($r["name"]);

+ 51 - 44
application/entity/K_Skill.php

@@ -102,50 +102,57 @@
 
             global $db;
 
-            $s =
-              "SELECT " .
-              "  id, " .
-              "  name, " .
-              "  description, " .
-              "  slot, " .
-              "  cooltime, " .
-              "  hits, " .
-              "  passive, " .
-              "  aoe, " .
-              "  max_level, " .
-              "  multiplier_formula " .
-              "FROM k_skill " .
-              "WHERE id = $id; ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
-            $this->id = $r["id"];
-            $this->name = HTML::e($r["name"]);
-            $this->description = HTML::e($r["description"]);
-            $this->slot = $r["slot"];
-            $this->cooltime = $r["cooltime"];
-            $this->hits = $r["hits"];
-            $this->passive = $r["passive"];
-            $this->aoe = $r["aoe"];
-            $this->max_level = $r["max_level"];
-            $this->multiplier_formula = HTML::e($r["multiplier_formula"]);
-            $s =
-              "SELECT " .
-              "  skill, " .
-              "  level " .
-              "FROM k_skill_level " .
-              "WHERE skill = " . $this->id . " " .
-              "ORDER BY level; ";
-            $q = $db->query($s);
-            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
-                array_push($this->level, new K_Skill_Level($r["skill"], $r["level"]));
-            }
-            $s =
-              "SELECT DISTINCT effect " .
-              "FROM k_skill_effect " .
-              "WHERE skill = " . $this->id . "; ";
-            $q = $db->query($s);
-            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
-                array_push($this->effect, new K_Effect($r["effect"]));
+            $statement = $db->prepare("
+              SELECT
+                id,
+                name,
+                description,
+                slot,
+                cooltime,
+                hits,
+                passive,
+                aoe,
+                max_level,
+                multiplier_formula
+              FROM k_skill
+              WHERE id = :id;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
+            if ($r){
+                $this->id = $r["id"];
+                $this->name = HTML::e($r["name"]);
+                $this->description = HTML::e($r["description"]);
+                $this->slot = $r["slot"];
+                $this->cooltime = $r["cooltime"];
+                $this->hits = $r["hits"];
+                $this->passive = $r["passive"];
+                $this->aoe = $r["aoe"];
+                $this->max_level = $r["max_level"];
+                $this->multiplier_formula = HTML::e($r["multiplier_formula"]);
+                $statement = $db->prepare("
+                  SELECT
+                    skill,
+                    level
+                  FROM k_skill_level
+                  WHERE skill = :skill
+                  ORDER BY level;
+                ");
+                $statement->bindValue(':skill', $this->id, SQLITE3_INTEGER);
+                $q = $statement->execute();
+                while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                    array_push($this->level, new K_Skill_Level($r["skill"], $r["level"]));
+                }
+                $statement = $db->prepare("
+                  SELECT DISTINCT effect
+                  FROM k_skill_effect
+                  WHERE skill = :skill;
+                ");
+                $statement->bindValue(':skill', $this->id, SQLITE3_INTEGER);
+                $q = $statement->execute();
+                while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                    array_push($this->effect, new K_Effect($r["effect"]));
+                }
             }
         }
 

+ 13 - 11
application/entity/K_Skill_Level.php

@@ -50,17 +50,19 @@
 
             global $db;
 
-            $s =
-              "SELECT " .
-              "  skill, " .
-              "  level, " .
-              "  description " .
-              "FROM k_skill_level " .
-              "WHERE " .
-              "  skill = $skill AND " .
-              "  level = $level;";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
+            $statement = $db->prepare("
+              SELECT
+                skill,
+                level,
+                description
+              FROM k_skill_level
+              WHERE
+                skill = :skill AND
+                level = :level;
+            ");
+            $statement->bindValue(':skill', $skill, SQLITE3_INTEGER);
+            $statement->bindValue(':level', $level, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
             if ($r){
                 $this->skill = $r["skill"];
                 $this->level = $r["level"];

+ 11 - 10
application/entity/K_Source.php

@@ -53,16 +53,17 @@
          */
         public function __construct($id){
             global $db;
-            $s =
-              "SELECT " .
-              "  id, " .
-              "  name, " .
-              "  description, " .
-              "  farmable " .
-              "FROM k_source " .
-              "WHERE id = $id; ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
+            $statement = $db->prepare("
+              SELECT
+                id,
+                name,
+                description,
+                farmable
+              FROM k_source
+              WHERE id = :id;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
             if ($r){
                 $this->id = $r["id"];
                 $this->name = HTML::e($r["name"]);

+ 67 - 60
application/entity/K_Unit.php

@@ -192,7 +192,7 @@
         public $craft_cost;
 
         /**
-         * @var \K_Unit_Skill[] Unit skills.
+         * @var \K_Skill[] Unit skills.
          */
         public $skill = [];
 
@@ -236,44 +236,45 @@
          */
         public function __construct($id, $complete = true){
             global $db;
-            $s =
-              "SELECT " .
-              "  id, " .
-              "  family, " .
-              "  name, " .
-              "  element, " .
-              "  archetype, " .
-              "  base_stars, " .
-              "  natural_stars, " .
-              "  obtainable, " .
-              "  can_awaken, " .
-              "  awaken_bonus, " .
-              "  skill_ups_to_max, " .
-              "  leader_skill, " .
-              "  hp, " .
-              "  attack, " .
-              "  defense, " .
-              "  speed, " .
-              "  crit_rate, " .
-              "  crit_damage, " .
-              "  resistance, " .
-              "  accuracy, " .
-              "  raw_hp, " .
-              "  raw_attack, " .
-              "  raw_defense, " .
-              "  max_lvl_hp, " .
-              "  max_lvl_attack, " .
-              "  max_lvl_defense, " .
-              "  awakens_from, " .
-              "  awakens_to, " .
-              "  fusion_food, " .
-              "  homunculus, " .
-              "  craft_cost " .
-              "FROM k_unit " .
-              "WHERE id = $id; ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
             $this->id = $id;
+            $statement = $db->prepare("
+              SELECT
+                id,
+                family,
+                name,
+                element,
+                archetype,
+                base_stars,
+                natural_stars,
+                obtainable,
+                can_awaken,
+                awaken_bonus,
+                skill_ups_to_max,
+                leader_skill,
+                hp,
+                attack,
+                defense,
+                speed,
+                crit_rate,
+                crit_damage,
+                resistance,
+                accuracy,
+                raw_hp,
+                raw_attack,
+                raw_defense,
+                max_lvl_hp,
+                max_lvl_attack,
+                max_lvl_defense,
+                awakens_from,
+                awakens_to,
+                fusion_food,
+                homunculus,
+                craft_cost
+              FROM k_unit
+              WHERE id = :id;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
             if ($r){
                 $this->family = $r["family"];
                 $this->name = HTML::e($r["name"]);
@@ -390,15 +391,17 @@
         public function load_essences(){
             global $db;
             $this->essences = [];
-            $s =
-              "SELECT " .
-              "  item, " .
-              "  amount " .
-              "FROM k_unit_essence " .
-              "WHERE " .
-              "  unit = " . $this->id . " " .
-              "ORDER BY item; ";
-            $q = $db->query($s);
+            $statement = $db->prepare("
+              SELECT
+                item,
+                amount
+              FROM k_unit_essence
+              WHERE
+                unit = :unit
+              ORDER BY item;
+            ");
+            $statement->bindValue(':unit', $this->id, SQLITE3_INTEGER);
+            $q = $statement->execute();
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 $e = [
                     "item" => new K_Inventory($r["item"], INVENTORY_TYPE_ID::ESSENCES),
@@ -420,12 +423,13 @@
         public function load_transformation(){
             global $db;
             $this->transformation = null;
-            $s = "
+            $statement = $db->prepare("
               SELECT id
               FROM k_unit_transformation
-              WHERE  unit = " . $this->id . ";";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
+              WHERE  unit = :unit;
+            ");
+            $statement->bindValue(':unit', $this->id, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
             if ($r){
                 $this->transformation = new K_Unit_Transformation($r["id"]);
             }
@@ -443,17 +447,18 @@
         public function load_skills(){
             global $db;
             $this->skill = [];
-            $s = "
+            $statement = $db->prepare("
               SELECT skill
               FROM
                 k_skill,
                 k_unit_skill
               WHERE
                 id = skill AND
-                unit = " . $this->id . "
+                unit = :unit
               ORDER BY slot;
-            ";
-            $q = $db->query($s);
+            ");
+            $statement->bindValue(':unit', $this->id, SQLITE3_INTEGER);
+            $q = $statement->execute();
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 array_push($this->skill, new K_Skill($r["skill"]));
             }
@@ -471,11 +476,13 @@
         public function load_sources(){
             global $db;
             $this->sources = [];
-            $s =
-              "SELECT source " .
-              "FROM k_unit_source " .
-              "WHERE unit = " . $this->id . ";";
-            $q = $db->query($s);
+            $statement = $db->prepare("
+              SELECT source
+              FROM k_unit_source
+              WHERE unit = :unit;
+            ");
+            $statement->bindValue(':unit', $this->id, SQLITE3_INTEGER);
+            $q = $statement->execute();
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 array_push($this->sources, new K_Source($r["source"]));
             }

+ 6 - 6
application/entity/K_Unit_Transformation.php

@@ -41,7 +41,7 @@
         public $unit;
 
         /**
-         * @var \K_Monster_Skill[] Skills in the transformed form.
+         * @var \K_Skill[] Skills in the transformed form.
          */
         public $skill = [];
 
@@ -56,16 +56,16 @@
          */
         public function __construct($id){
             global $db;
-            $s = "
+            $statement = $db->prepare("
               SELECT
                 id,
                 family,
                 unit
               FROM k_unit_transformation
-              WHERE id = $id;
-            ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
+               WHERE id = :id;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
             if ($r){
                 $this->id = $r["id"];
                 $this->family = $r["family"];

+ 58 - 46
application/entity/Player.php

@@ -185,7 +185,7 @@
         public function __construct($id, $complete = true){
             global $UID;
             global $db;
-            $s = "
+            $statement = $db->prepare("
               SELECT
                 uid,
                 name,
@@ -220,10 +220,10 @@
                 top_rank_toan,
                 top_rank_toah
               FROM player
-              WHERE uid = '$UID';
-            ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
+              WHERE uid = :uid;
+            ");
+            $statement->bindValue(':uid', $UID, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
             if ($r){
                 $this->uid = $r["uid"];
                 $this->name = $r["name"];
@@ -260,25 +260,27 @@
                 $this->top_rank_toan = $r["top_rank_toan"];
                 $this->top_rank_toah = $r["top_rank_toah"];
             }
-            $s = "
+            $statement = $db->prepare("
                 SELECT unit
                 FROM defense
-                WHERE uid = '$UID'
+                WHERE uid = :uid
                 ORDER BY position;
-            ";
-            $q = $db->query($s);
+            ");
+            $statement->bindValue(':uid', $UID, SQLITE3_INTEGER);
+            $q = $statement->execute();
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 array_push($this->defense, new Unit($r["unit"], false));
             }
-            $s = "
+            $statement = $db->prepare("
                 SELECT
                   unit,
                   position
                 FROM gw_defense
-                WHERE uid = '$UID'
+                WHERE uid = :uid
                 ORDER BY position;
-            ";
-            $q = $db->query($s);
+            ");
+            $statement->bindValue(':uid', $UID, SQLITE3_INTEGER);
+            $q = $statement->execute();
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 if ($r["position"] <= 3){
                     array_push($this->gw_defense[0], new Unit($r["unit"], false));
@@ -287,7 +289,7 @@
                     array_push($this->gw_defense[1], new Unit($r["unit"], false));
                 }
             }
-            $s = "
+            $statement = $db->prepare("
                 SELECT
                   building.id,
                   min(building)
@@ -295,72 +297,80 @@
                   building,
                   k_building
                 WHERE
-                  uid = '$UID' AND
+                  uid = :uid AND
                   building.building = k_building.id 
                 GROUP BY building.building;
-            ";
-            $q = $db->query($s);
+            ");
+            $statement->bindValue(':uid', $UID, SQLITE3_INTEGER);
+            $q = $statement->execute();
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 array_push($this->building, new Building($r["id"]));
             }
-            $s = "
+            $statement = $db->prepare("
                 SELECT id
                 FROM decoration
-                WHERE uid = '$UID';
-            ";
-            $q = $db->query($s);
+                WHERE uid = :uid;
+            ");
+            $statement->bindValue(':uid', $UID, SQLITE3_INTEGER);
+            $q = $statement->execute();
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 array_push($this->decoration, new Decoration($r["id"]));
             }
-            $s = "
+            $statement = $db->prepare("
                 SELECT
                   id,
                   type
                 FROM inventory
                 WHERE
-                  uid = '$UID' AND
-                  type = " . INVENTORY_TYPE_ID::SCROLL . " AND
+                  uid = :uid AND
+                  type = :type AND
                   amount > 0
                 ORDER BY inventory.type;
-            ";
-            $q = $db->query($s);
+            ");
+            $statement->bindValue(':uid', $UID, SQLITE3_INTEGER);
+            $statement->bindValue(':type', INVENTORY_TYPE_ID::SCROLL, SQLITE3_INTEGER);
+            $q = $statement->execute();
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 array_push($this->inventory_scroll, new Inventory($r["id"], $r["type"]));
             }
             // Rune crafting items are marked as generic crafting.
-            $s = "
+            $statement = $db->prepare("
                 SELECT
                   inventory.id AS id,
                   inventory.type AS type
                 FROM
                   inventory
                 WHERE
-                  inventory.uid = '$UID' AND
-                  inventory.type = " . INVENTORY_TYPE_ID::RUNE_CRAFT . " AND
+                  inventory.uid = :uid AND
+                  inventory.type = :type AND
                   amount > 0
                 ORDER BY inventory.id;
-            ";
-            $q = $db->query($s);
+            ");
+            $statement->bindValue(':uid', $UID, SQLITE3_INTEGER);
+            $statement->bindValue(':type', INVENTORY_TYPE_ID::ENCHANTMENT, SQLITE3_INTEGER);
+            $q = $statement->execute();
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 array_push($this->inventory_craft_rune, new Inventory($r["id"], $r["type"]));
             }
-            $s = "
+            $statement = $db->prepare("
                 SELECT
                   inventory.id AS id,
                   inventory.type AS type
                 FROM
                   inventory
                 WHERE
-                  inventory.uid = '$UID' AND
-                  inventory.type = " . INVENTORY_TYPE_ID::CRAFT_STUFF . " AND
+                  inventory.uid = :uid AND
+                  inventory.type = :type AND
                   amount > 0
                 ORDER BY inventory.id;
-            ";
-            $q = $db->query($s);
+            ");
+            $statement->bindValue(':uid', $UID, SQLITE3_INTEGER);
+            $statement->bindValue(':type', INVENTORY_TYPE_ID::CRAFT_STUFF, SQLITE3_INTEGER);
+            $q = $statement->execute();
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 array_push($this->inventory_craft, new Inventory($r["id"], $r["type"]));
             }
-            $s = "
+            $statement = $db->prepare("
                 SELECT
                   inventory.id AS id,
                   inventory.type AS type
@@ -370,8 +380,8 @@
                 WHERE
                   inventory.id = k_inventory.id AND
                   inventory.type = k_inventory.type AND
-                  inventory.uid = '$UID' AND
-                  inventory.type = " . INVENTORY_TYPE_ID::ESSENCES . " AND
+                  inventory.uid = :uid AND
+                  inventory.type = :type AND
                   amount > 0
                 ORDER BY
                   upper(name) NOT LIKE '%MAGIC%',
@@ -384,21 +394,22 @@
                   upper(name) NOT LIKE '%MID%',
                   upper(name) NOT LIKE '%HIGH%'
                 ;
-            ";
-            $q = $db->query($s);
+            ");
+            $statement->bindValue(':uid', $UID, SQLITE3_INTEGER);
+            $statement->bindValue(':type', INVENTORY_TYPE_ID::ESSENCES, SQLITE3_INTEGER);
+            $q = $statement->execute();
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 array_push($this->inventory_essence, new Inventory($r["id"], $r["type"]));
             }
-            $s = "
+            $statement = $db->prepare("
                 SELECT
                   uid,
                   area_type,
                   area
                 FROM record
-                WHERE uid = '$UID'
+                WHERE uid = :uid
                 ORDER BY
                   CASE
-
                     WHEN (area_type = 2 AND area = 8001) THEN  1 -- Giant's Keep
                     WHEN (area_type = 2 AND area = 9001) THEN  2 -- Dragon's Lair
                     WHEN (area_type = 2 AND area = 6001) THEN  3 -- Necropolis
@@ -418,8 +429,9 @@
                     WHEN (area_type = 2 AND area = 1001) THEN 17 -- Hall of Dark
                     ELSE area 
                     END ASC
-            ";
-            $q = $db->query($s);
+            ");
+            $statement->bindValue(':uid', $UID, SQLITE3_INTEGER);
+            $q = $statement->execute();
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 array_push($this->record, new Record($r["uid"], $r["area_type"], $r["area"]));
             }

+ 49 - 43
application/entity/Record.php

@@ -85,7 +85,7 @@
 
             global $db;
 
-            $s = "
+            $statement = $db->prepare("
                 SELECT
                   uid,
                   area_type,
@@ -96,48 +96,54 @@
                   rank
                 FROM record
                 WHERE
-                  uid = $uid AND
-                  area_type = $area_type AND
-                  area = $area;
-            ";
-            error_log($s);
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
-            $this->uid = $r["uid"];
-            $this->area = new K_Area($r["area"], $r["area_type"]);
-            $this->stage = $r["stage"];
-            $this->time = $r["time"];
-            $this->score = $r["score"];
-            $this->rank = $r["rank"];
-            $s = "
-                SELECT
-                  unit,
-                  k_unit,
-                  (SELECT count(id) FROM unit WHERE unit.id = record_party.unit) AS owned,
-                  leader,
-                  front
-                FROM record_party
-                WHERE
-                  uid = $uid AND
-                  area_type = $area_type AND
-                  area = $area
-                ORDER BY 
-                  front DESC,
-                  leader DESC;
-            ";
-            $q = $db->query($s);
-            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
-                if ($r["owned"] > 0){
-                    array_push($this->party, new Unit($r["unit"], false));
-                }
-                else{
-                    array_push($this->party, new K_Unit($r["k_unit"], false));
-                }
-                if ($r["leader"] == 1){
-                    $this->leader = $r["unit"];
-                }
-                if ($r["front"] == 1){
-                    array_push($this->frontline, $r["unit"]);
+                  uid = :uid AND
+                  area_type = :area_type AND
+                  area = :area;
+            ");
+            $statement->bindValue(':uid', $uid, SQLITE3_INTEGER);
+            $statement->bindValue(':area_type', $area_type, SQLITE3_INTEGER);
+            $statement->bindValue(':area', $area, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
+            if ($r){
+                $this->uid = $r["uid"];
+                $this->area = new K_Area($r["area"], $r["area_type"]);
+                $this->stage = $r["stage"];
+                $this->time = $r["time"];
+                $this->score = $r["score"];
+                $this->rank = $r["rank"];
+                $statement = $db->prepare("
+                    SELECT
+                      unit,
+                      k_unit,
+                      (SELECT count(id) FROM unit WHERE unit.id = record_party.unit) AS owned,
+                      leader,
+                      front
+                    FROM record_party
+                    WHERE
+                      uid = :uid AND
+                      area_type = :area_type AND
+                      area = :area
+                    ORDER BY 
+                      front DESC,
+                      leader DESC;
+                ");
+                $statement->bindValue(':uid', $uid, SQLITE3_INTEGER);
+                $statement->bindValue(':area_type', $area_type, SQLITE3_INTEGER);
+                $statement->bindValue(':area', $area, SQLITE3_INTEGER);
+                $q = $statement->execute();
+                while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                    if ($r["owned"] > 0){
+                        array_push($this->party, new Unit($r["unit"], false));
+                    }
+                    else{
+                        array_push($this->party, new K_Unit($r["k_unit"], false));
+                    }
+                    if ($r["leader"] == 1){
+                        $this->leader = $r["unit"];
+                    }
+                    if ($r["front"] == 1){
+                        array_push($this->frontline, $r["unit"]);
+                    }
                 }
             }
         }

+ 267 - 205
application/entity/Run.php

@@ -14,6 +14,7 @@
     require_once(PATH::ENTITY . "Unit.php");
     require_once(PATH::ENTITY . "K_Unit.php");
     require_once(PATH::ENTITY . "Rune.php");
+    require_once(PATH::ENTITY . "Enchantment.php");
     require_once(PATH::ENTITY . "Inventory.php");
     require_once(PATH::ENTITY . "K_Area.php");
     require_once(PATH::ENTITY . "K_Rune_Set.php");
@@ -71,17 +72,27 @@
         /**
          * @var int Reward mana.
          */
-        public $mana;
+        public $mana = 0;
 
         /**
          * @var int Reward energy.
          */
-        public $energy;
+        public $energy = 0;
 
         /**
          * @var int Reward crystal.
          */
-        public $crystal;
+        public $crystal = 0;
+
+        /**
+         * @var int Reward Honor Points (Arena).
+         */
+        public $honor_points = 0;
+
+        /**
+         * @var int Reward Guild Points.
+         */
+        public $guild_points = 0;
 
         /**
          * @var bool Indicates if a frind/mentor helped.
@@ -146,7 +157,12 @@
         /**
          * @var \Rune[] List of Runes dropped.
          */
-        public $rune= [];
+        public $rune = [];
+
+        /**
+         * @var \Enchantment[] List of Enchantments dropped.
+         */
+        public $enchantment = [];
 
         /**
          * Constructor.
@@ -161,213 +177,259 @@
 
             global $db;
 
-            $s = "
-                SELECT
-                  id,
-                  uid,
-                  dtime,
-                  area,
-                  stage,
-                  difficulty,
-                  win,
-                  time,
-                  mana,
-                  energy,
-                  crystal,
-                  helper,
-                  score,
-                  rank
-                FROM run
-                WHERE id = $id;
-            ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
-            $this->id = $r["id"];
-            $this->uid = $r["uid"];
-            $this->dtime = $r["dtime"];
-            $type = 0;
-            if (APPLICATION::valid_id("RAID_RIFT_DUNGEON_ID", $r["area"]) && $r["stage"] < 1){
-                $type = AREA_TYPE_ID::RIFT_RAID_DUNGEON;
-            }
-            elseif (APPLICATION::valid_id("SCENARIO_ID", $r["area"])){
-                $type = AREA_TYPE_ID::SCENARIO;
-            }
-            elseif (APPLICATION::valid_id("ELEMENTAL_RIFT_DUNGEON_ID", $r["area"]) && $r["stage"] < 1){
-                $type = AREA_TYPE_ID::RIFT_DUNGEON;
-            }
-            elseif (APPLICATION::valid_id("CAIROS_DUNGEON_ID", $r["area"])){
-                $type = AREA_TYPE_ID::CAIROS_DUNGEON;
-            }
-            elseif (APPLICATION::valid_id("DIMENSION_DUNGEON_ID", $r["area"])){
-                if ($this->id == 1486){
-                    error_log("VALID AREA ID: " . $r["area"] . ", " . AREA_TYPE_ID::DIMENSIONAL_HOLE);
+            $statement = $db->prepare("
+              SELECT
+                id,
+                uid,
+                dtime,
+                area_type,
+                area,
+                stage,
+                difficulty,
+                win,
+                time,
+                mana,
+                energy,
+                crystal,
+                honor_points,
+                guild_points,
+                helper,
+                score,
+                rank
+              FROM run
+              WHERE id = :id;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
+            if ($r){
+                $this->id = $r["id"];
+                $this->uid = $r["uid"];
+                $this->dtime = $r["dtime"];
+                $this->area = new K_Area($r["area"], $r["area_type"]);
+                $this->stage = $r["stage"];
+                $this->difficulty = $r["difficulty"];
+                $this->win = $r["win"];
+                $this->time = $r["time"];
+                $this->mana = $r["mana"];
+                $this->energy = $r["energy"];
+                $this->crystal = $r["crystal"];
+                $this->honor_points = $r["honor_points"];
+                $this->guild_points = $r["guild_points"];
+                $this->helper = $r["helper"];
+                $this->score = $r["score"];
+                $this->rank = $r["rank"];
+                $statement = $db->prepare("
+                    SELECT
+                      unit,
+                      k_unit,
+                      (SELECT count(id) FROM unit WHERE unit.id = run_party.unit) AS owned,
+                      leader,
+                      front
+                    FROM run_party
+                    WHERE run = :run
+                    ORDER BY 
+                      front DESC,
+                      leader DESC;
+                ");
+                $statement->bindValue(':run', $id, SQLITE3_INTEGER);
+                $q = $statement->execute();
+                while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                    if ($r["owned"] > 0){
+                        array_push($this->party, new Unit($r["unit"], false));
+                    }
+                    else{
+                        array_push($this->party, new K_Unit($r["k_unit"], false));
+                    }
+                    if ($r["leader"] == 1){
+                        $this->leader = $r["unit"];
+                    }
+                    if ($r["front"] == 1){
+                        array_push($this->frontline, $r["unit"]);
+                    }
                 }
-                $type = AREA_TYPE_ID::DIMENSIONAL_HOLE;
-            }
-            if ($this->id == 1486){
-                error_log("INSTANCING DH AREA: " . $r["area"] . ", " . $type);
-            }
-            $this->area = new K_Area($r["area"], $type);
-            $this->stage = $r["stage"];
-            $this->difficulty = $r["difficulty"];
-            $this->win = $r["win"];
-            $this->time = $r["time"];
-            $this->mana = $r["mana"];
-            $this->energy = $r["energy"];
-            $this->crystal = $r["crystal"];
-            $this->helper = $r["helper"];
-            $this->score = $r["score"];
-            $this->rank = $r["rank"];
-            $s = "
-                SELECT
-                  unit,
-                  k_unit,
-                  (SELECT count(id) FROM unit WHERE unit.id = run_party.unit) AS owned,
-                  leader,
-                  front
-                FROM run_party
-                WHERE run = $id
-                ORDER BY 
-                  front DESC,
-                  leader DESC;
-            ";
-            $q = $db->query($s);
-            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
-                if ($r["owned"] > 0){
-                    array_push($this->party, new Unit($r["unit"], false));
+                $statement = $db->prepare("
+                    SELECT
+                      item,
+                      amount
+                    FROM run_drop_item
+                    WHERE run = :run;
+                ");
+                $statement->bindValue(':run', $this->id, SQLITE3_INTEGER);
+                $q = $statement->execute();
+                while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                    $i = new Inventory($r["item"]);
+                    $i->amount =  $r["amount"];
+                    array_push($this->item, $i);
                 }
-                else{
-                    array_push($this->party, new K_Unit($r["k_unit"], false));
+                $statement = $db->prepare("
+                    SELECT unit
+                    FROM run_drop_unit
+                    WHERE run = :run;
+                ");
+                $statement->bindValue(':run', $this->id, SQLITE3_INTEGER);
+                $q = $statement->execute();
+                while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                    array_push($this->unit, new K_Unit($r["unit"]));
                 }
-                if ($r["leader"] == 1){
-                    $this->leader = $r["unit"];
+                $statement = $db->prepare("
+                    SELECT unit
+                    FROM run_drop_sd
+                    WHERE run = :run;
+                ");
+                $statement->bindValue(':run', $this->id, SQLITE3_INTEGER);
+                $q = $statement->execute();
+                while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                    array_push($this->sd, new K_Unit($r["unit"]));
                 }
-                if ($r["front"] == 1){
-                    array_push($this->frontline, $r["unit"]);
+                $statement = $db->prepare("
+                    SELECT
+                      unit,
+                      amount
+                    FROM run_drop_unit_pieces
+                    WHERE run = :run;
+                ");
+                $statement->bindValue(':run', $this->id, SQLITE3_INTEGER);
+                $q = $statement->execute();
+                while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                    $i = [
+                        "unit" => new K_Unit($r["unit"]),
+                        "amount" => $r["amount"]
+                    ];
+                    array_push($this->unit_piece, $i);
                 }
-            }
-            $s = "
-                SELECT
-                  item,
-                  amount
-                FROM run_drop_item
-                WHERE run = $this->id;
-            ";
-            $q = $db->query($s);
-            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
-                $i = new Inventory($r["item"]);
-                $i->amount =  $r["amount"];
-                array_push($this->item, $i);
-            }
-            $s = "
-                SELECT unit
-                FROM run_drop_unit
-                WHERE run = $this->id;
-            ";
-            $q = $db->query($s);
-            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
-                array_push($this->unit, new K_Unit($r["unit"]));
-            }
-            $s = "
-                SELECT unit
-                FROM run_drop_sd
-                WHERE run = $this->id;
-            ";
-            $q = $db->query($s);
-            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
-                array_push($this->sd, new K_Unit($r["unit"]));
-            }
-            $s = "
-                SELECT
-                  unit,
-                  amount
-                FROM run_drop_unit_pieces
-                WHERE run = $this->id;
-            ";
-            $q = $db->query($s);
-            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
-                $i = [
-                    "unit" => new K_Unit($r["unit"]),
-                    "amount" => $r["amount"]
-                ];
-                array_push($this->unit_piece, $i);
-            }
-            $s = "
-                SELECT amount
-                FROM run_drop_shapeshifting
-                WHERE run = $this->id;
-            ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
-            if ($r){
-                $this->shapeshifting = $r["amount"];
-            }
-            $s = "
-                SELECT
-                  id,
-                  type,
-                  slot,
-                  stars,
-                  ancient,
-                  quality,
-                  value,
-                  efficiency,
-                  main_stat,
-                  main_stat_value,
-                  innate_stat,
-                  innate_stat_value,
-                  substat_1,
-                  substat_1_value,
-                  substat_2,
-                  substat_2_value,
-                  substat_3,
-                  substat_3_value,
-                  substat_4,
-                  substat_4_value
-                FROM run_drop_rune
-                WHERE run = $this->id;
-            ";
-            $q = $db->query($s);
-            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
-                $rune = new Rune($r["id"]);
-                if (!isset($rune->id)){
-                    $rune->id = $r["id"];
-                    $rune->level = 0;
-                    $rune->assigned_to = null;
-                    $rune->type = new K_Rune_Set($r["type"]);
-                    $rune->slot = $r["slot"];
-                    $rune->stars = $r["stars"];
-                    $rune->ancient = $r["ancient"];
-                    $rune->quality = $r["quality"];
-                    $rune->original_quality = $r["quality"];
-                    $rune->value = $r["value"];
-                    $rune->value = 0;
-                    $rune->efficiency = $r["efficiency"];
-                    // TODO: Calculate?
-                    $rune->max_efficiency = $r["efficiency"];
-                    $rune->main_stat = $r["main_stat"];
-                    $rune->main_stat_value = $r["main_stat_value"];
-                    $rune->innate_stat = $r["innate_stat"];
-                    $rune->innate_stat_value = $r["innate_stat_value"];
-                    $rune->substat_1 = $r["substat_1"];
-                    $rune->substat_1_value = $r["substat_1_value"];
-                    $rune->substat_1_enchant = 0;
-                    $rune->substat_1_grind = 0;
-                    $rune->substat_2 = $r["substat_2"];
-                    $rune->substat_2_value = $r["substat_2_value"];
-                    $rune->substat_2_enchant = 0;
-                    $rune->substat_2_grind = 0;
-                    $rune->substat_3 = $r["substat_3"];
-                    $rune->substat_3_value = $r["substat_3_value"];
-                    $rune->substat_3_enchant = 0;
-                    $rune->substat_3_grind = 0;
-                    $rune->substat_4 = $r["substat_4"];
-                    $rune->substat_4_value = $r["substat_4_value"];
-                    $rune->substat_4_enchant = 0;
-                    $rune->substat_4_grind = 0;
-                    $rune->refresh_names();
+                $statement = $db->prepare("
+                    SELECT amount
+                    FROM run_drop_shapeshifting
+                    WHERE run = :run;
+                ");
+                $statement->bindValue(':run', $this->id, SQLITE3_INTEGER);
+                $q = $statement->execute();
+                $r = $q->fetchArray(SQLITE3_ASSOC);
+                if ($r){
+                    $this->shapeshifting = $r["amount"];
+                }
+                $statement = $db->prepare("
+                    SELECT
+                      id,
+                      type,
+                      slot,
+                      stars,
+                      ancient,
+                      quality,
+                      value,
+                      efficiency,
+                      main_stat,
+                      main_stat_value,
+                      innate_stat,
+                      innate_stat_value,
+                      substat_1,
+                      substat_1_value,
+                      substat_2,
+                      substat_2_value,
+                      substat_3,
+                      substat_3_value,
+                      substat_4,
+                      substat_4_value
+                    FROM run_drop_rune
+                    WHERE run = :run;
+                ");
+                $statement->bindValue(':run', $this->id, SQLITE3_INTEGER);
+                $q = $statement->execute();
+                while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                    $rune = new Rune($r["id"]);
+                    if (!isset($rune->id)){
+                        $rune->id = $r["id"];
+                        $rune->level = 0;
+                        $rune->assigned_to = null;
+                        $rune->type = new K_Rune_Set($r["type"]);
+                        $rune->slot = $r["slot"];
+                        $rune->stars = $r["stars"];
+                        $rune->ancient = $r["ancient"];
+                        $rune->quality = $r["quality"];
+                        $rune->original_quality = $r["quality"];
+                        $rune->value = $r["value"];
+                        $rune->value = 0;
+                        $rune->efficiency = $r["efficiency"];
+                        // TODO: Calculate?
+                        $rune->max_efficiency = $r["efficiency"];
+                        $rune->main_stat = $r["main_stat"];
+                        $rune->main_stat_value = $r["main_stat_value"];
+                        $rune->innate_stat = $r["innate_stat"];
+                        $rune->innate_stat_value = $r["innate_stat_value"];
+                        $rune->substat_1 = $r["substat_1"];
+                        $rune->substat_1_value = $r["substat_1_value"];
+                        $rune->substat_1_enchant = 0;
+                        $rune->substat_1_grind = 0;
+                        $rune->substat_2 = $r["substat_2"];
+                        $rune->substat_2_value = $r["substat_2_value"];
+                        $rune->substat_2_enchant = 0;
+                        $rune->substat_2_grind = 0;
+                        $rune->substat_3 = $r["substat_3"];
+                        $rune->substat_3_value = $r["substat_3_value"];
+                        $rune->substat_3_enchant = 0;
+                        $rune->substat_3_grind = 0;
+                        $rune->substat_4 = $r["substat_4"];
+                        $rune->substat_4_value = $r["substat_4_value"];
+                        $rune->substat_4_enchant = 0;
+                        $rune->substat_4_grind = 0;
+                        $rune->refresh_names();
+                    }
+                    array_push($this->rune, $rune);
+                }
+                $statement = $db->prepare("
+                  SELECT
+                    run_drop_enchantment.id AS id,
+                    type,
+                    run_drop_enchantment.quality AS quality,
+                    rune,
+                    run_drop_enchantment.stat AS stat,
+                    value,
+                    k_enchantment_type.inmemorial AS inmemorial,
+                    k_enchantment_type.ancient AS ancient,
+                    k_enchantment_range.min AS min,
+                    k_enchantment_range.max AS max
+                  FROM
+                    run_drop_enchantment,
+                    k_enchantment_type,
+                    k_enchantment_range
+                  WHERE
+                    run = :run AND
+                    run_drop_enchantment.type = k_enchantment_type.id AND
+                    k_enchantment_type.gem = k_enchantment_range.gem AND
+                    k_enchantment_type.gem = k_enchantment_range.gem AND
+                    k_enchantment_type.ancient = k_enchantment_range.ancient AND
+                    run_drop_enchantment.quality = k_enchantment_range.quality AND
+                    run_drop_enchantment.stat = k_enchantment_range.stat;
+                ");
+                $statement->bindValue(':run', $this->id, SQLITE3_INTEGER);
+                $q = $statement->execute();
+                while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                    $enchantment = new Enchantment($r["id"]);
+                    if (!isset($enchantment->id)){
+                        $enchantment->id = $r["id"];
+                        if ($r["type"] == CRAFT_ID::ENCHANT_GEM || $r["type"] == CRAFT_ID::IMMEMORIAL_GEM){
+                                $enchantment->gem = 1;
+                        }
+                        else{
+                                $enchantment->gem = 0;
+                        }
+                        $enchantment->quality = $r["quality"];
+                        $enchantment->value = 0;
+                        $enchantment->stat = $r["stat"];
+                        $enchantment->ancient = $r["ancient"];
+                        $enchantment->inmemorial = $r["inmemorial"];
+                        $enchantment->rune = new K_Rune_Set($r["rune"]);
+                        $enchantment->min = $r["min"];
+                        $enchantment->max = $r["max"];
+                        $enchantment->value = $r["value"];
+                        $enchantment->amount = 0;
+                        $enchantment->refresh_names();
+                    }
+                    else{
+                        $enchantment->amount = 0;
+                    }
+                    array_push($this->enchantment, $enchantment);
                 }
-                array_push($this->rune, $rune);
             }
         }
 

+ 47 - 46
application/entity/Rune.php

@@ -65,14 +65,14 @@
         /**
          * @var int Rune quality.
          *
-         * @see QUALITY
+         * @see QUALITY_ID
          */
         public $quality;
 
         /**
          * @var int Rune original quality.
          *
-         * @see QUALITY
+         * @see QUALITY_ID
          */
         public $original_quality;
 
@@ -104,7 +104,7 @@
         /**
          * @var int Main stat of the rune.
          *
-         * @see RUNE_STAT
+         * @see RUNE_STAT_ID
          */
         public $main_stat;
 
@@ -121,7 +121,7 @@
         /**
          * @var int Extra stat of the rune.
          *
-         * @see RUNE_STAT
+         * @see RUNE_STAT_ID
          */
         public $innate_stat;
 
@@ -143,7 +143,7 @@
         /**
          * @var int Substat 1 of the rune.
          *
-         * @see RUNE_STAT
+         * @see RUNE_STAT_ID
          */
         public $substat_1;
 
@@ -165,7 +165,7 @@
         /**
          * @var int Substat 2 of the rune.
          *
-         * @see RUNE_STAT
+         * @see RUNE_STAT_ID
          */
         public $substat_2;
 
@@ -187,7 +187,7 @@
         /**
          * @var int Substat 3 of the rune.
          *
-         * @see RUNE_STAT
+         * @see RUNE_STAT_ID
          */
         public $substat_3;
 
@@ -209,7 +209,7 @@
         /**
          * @var int Substat 4 of the rune.
          *
-         * @see RUNE_STAT
+         * @see RUNE_STAT_ID
          */
         public $substat_4;
 
@@ -241,44 +241,45 @@
 
             global $db;
 
-            $s =
-              "SELECT " .
-              "  id, " .
-              "  assigned_to, " .
-              "  type, " .
-              "  slot, " .
-              "  stars, " .
-              "  level, " .
-              "  ancient, " .
-              "  quality, " .
-              "  original_quality, " .
-              "  value, " .
-              "  efficiency, " .
-              "  max_efficiency, " .
-              "  main_stat, " .
-              "  main_stat_value, " .
-              "  innate_stat, " .
-              "  innate_stat_value, " .
-              "  substat_1, " .
-              "  substat_1_value, " .
-              "  substat_1_enchant, " .
-              "  substat_1_grind, " .
-              "  substat_2, " .
-              "  substat_2_value, " .
-              "  substat_2_enchant, " .
-              "  substat_2_grind, " .
-              "  substat_3, " .
-              "  substat_3_value, " .
-              "  substat_3_enchant, " .
-              "  substat_3_grind, " .
-              "  substat_4, " .
-              "  substat_4_value, " .
-              "  substat_4_enchant, " .
-              "  substat_4_grind " .
-              "FROM rune " .
-              "WHERE id = '$id'; ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
+            $statement = $db->prepare("
+              SELECT
+                id,
+                assigned_to,
+                type,
+                slot,
+                stars,
+                level,
+                ancient,
+                quality,
+                original_quality,
+                value,
+                efficiency,
+                max_efficiency,
+                main_stat,
+                main_stat_value,
+                innate_stat,
+                innate_stat_value,
+                substat_1,
+                substat_1_value,
+                substat_1_enchant,
+                substat_1_grind,
+                substat_2,
+                substat_2_value,
+                substat_2_enchant,
+                substat_2_grind,
+                substat_3,
+                substat_3_value,
+                substat_3_enchant,
+                substat_3_grind,
+                substat_4,
+                substat_4_value,
+                substat_4_enchant,
+                substat_4_grind
+              FROM rune
+              WHERE id = :id;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
             if ($r){
                 $this->id = $r["id"];
                 $this->assigned_to = $r["assigned_to"];

+ 36 - 33
application/entity/Team.php

@@ -58,7 +58,7 @@
         public $frontline = [];
 
         /**
-         * @var Area Area the team is designed for.
+         * @var \K_Area Area the team is designed for.
          */
         public $area;
 
@@ -93,7 +93,7 @@
 
             global $db;
 
-            $s = "
+            $statement = $db->prepare("
               SELECT
                 uid,
                 id,
@@ -105,37 +105,40 @@
                 difficulty,
                 score
               FROM team
-              WHERE id = '$id';
-            ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
-            $this->uid = $r["uid"];
-            $this->id = $r["id"];
-            $this->name = HTML::e($r["name"]);
-            $this->description = HTML::e($r["description"]);
-            $this->area = new K_Area($r["area"], $r["area_type"]);
-            $this->stage = $r["stage"];
-            $this->score = $r["score"];
-            if ($complete){
-                $s = "
-                  SELECT
-                    unit,
-                    leader,
-                    front
-                  FROM team_unit
-                  WHERE team = '$id'
-                  ORDER BY 
-                    front DESC,
-                    leader DESC;
-                ";
-                $q = $db->query($s);
-                while ($r = $q->fetchArray(SQLITE3_ASSOC)){
-                    array_push($this->unit, new Unit($r["unit"], false));
-                    if ($r["leader"] == 1){
-                        $this->leader = $r["unit"];
-                    }
-                    if ($r["front"] == 1){
-                        array_push($this->frontline, $r["unit"]);
+              WHERE id = :id;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
+            if ($r){
+                $this->uid = $r["uid"];
+                $this->id = $r["id"];
+                $this->name = HTML::e($r["name"]);
+                $this->description = HTML::e($r["description"]);
+                $this->area = new K_Area($r["area"], $r["area_type"]);
+                $this->stage = $r["stage"];
+                $this->score = $r["score"];
+                if ($complete){
+                    $statement = $db->prepare("
+                      SELECT
+                        unit,
+                        leader,
+                        front
+                      FROM team_unit
+                      WHERE team = :team
+                      ORDER BY 
+                        front DESC,
+                        leader DESC;
+                    ");
+                    $statement->bindValue(':team', $this->id, SQLITE3_INTEGER);
+                    $q = $statement->execute();
+                    while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                        array_push($this->unit, new Unit($r["unit"], false));
+                        if ($r["leader"] == 1){
+                            $this->leader = $r["unit"];
+                        }
+                        if ($r["front"] == 1){
+                            array_push($this->frontline, $r["unit"]);
+                        }
                     }
                 }
             }

+ 105 - 81
application/entity/Unit.php

@@ -377,36 +377,38 @@
             if ($uid == 0){
                 $uid = $UID;
             }
-            $s =
-              "SELECT " .
-              "  uid, " .
-              "  id, " .
-              "  building, " .
-              "  unit, " .
-              "  stars, " .
-              "  level, " .
-              "  hp, " .
-              "  attack, " .
-              "  defense, " .
-              "  speed, " .
-              "  crit_rate, " .
-              "  crit_damage, " .
-              "  resistance, " .
-              "  accuracy, " .
-              "  experience, " .
-              "  exp_gained, " .
-              "  exp_gain_rate, " .
-              "  costume, " .
-              "  source, " .
-              "  create_time, " .
-              "  homunculus_name, " .
-              "  lock " .
-              "FROM unit " .
-              "WHERE " .
-              "  uid = '$uid' AND " .
-              "  id = '$id'; ";
-            $q = $db->query($s);
-            $r = $q->fetchArray(SQLITE3_ASSOC);
+             $statement = $db->prepare("
+              SELECT
+                uid,
+                id,
+                building,
+                unit,
+                stars,
+                level,
+                hp,
+                attack,
+                defense,
+                speed,
+                crit_rate,
+                crit_damage,
+                resistance,
+                accuracy,
+                experience,
+                exp_gained,
+                exp_gain_rate,
+                costume,
+                source,
+                create_time,
+                homunculus_name,
+                lock
+              FROM unit
+              WHERE
+                uid = :uid AND
+                id = :id;
+            ");
+            $statement->bindValue(':id', $id, SQLITE3_INTEGER);
+            $statement->bindValue(':uid', $uid, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
             if ($r){
                 $this->uid = $r["uid"];
                 $this->id = $r["id"];
@@ -438,29 +440,35 @@
                     $this->load_teams();
                     $this->check_storage();
                     $this->lock = $r["lock"];
-                    $s =
-                      "SELECT level " .
-                      "FROM " .
-                      "  unit_skill, " .
-                      "  k_skill " .
-                      "WHERE " .
-                      "  k_skill.id = unit_skill.skill AND " .
-                      "  unit = '$this->id' " .
-                      "ORDER BY slot;";
-                    $q = $db->query($s);
+                    $statement = $db->prepare("
+                      SELECT level
+                      FROM
+                        unit_skill,
+                        k_skill
+                      WHERE
+                        k_skill.id = unit_skill.skill AND
+                        unit = :unit
+                      ORDER BY slot;
+                    ");
+                    $statement->bindValue(':unit', $this->id, SQLITE3_INTEGER);
+                    $q = $statement->execute();
                     while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                         array_push($this->skill_levels, $r["level"]);
                     }
-                    $s =
-                      "SELECT exp " .
-                      "FROM k_experience " .
-                      "WHERE " .
-                      "  stars = $this->stars AND " .
-                      "  level = $this->level;";
-                    $q = $db->query($s);
-                    $r = $q->fetchArray(SQLITE3_ASSOC);
-                    $this->experience_level = $r["exp"];
-                    $this->experience_level_up = $r["exp"] - $this->exp_gained;
+                    $statement = $db->prepare("
+                      SELECT exp
+                      FROM k_experience
+                      WHERE
+                        stars = :stars AND
+                        level = :level;
+                    ");
+                    $statement->bindValue(':stars', $this->stars, SQLITE3_INTEGER);
+                    $statement->bindValue(':level', $this->level, SQLITE3_INTEGER);
+                    $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
+                    if ($r){
+                        $this->experience_level = $r["exp"];
+                        $this->experience_level_up = $r["exp"] - $this->exp_gained;
+                    }
                 }
                 if ($this->unit->homunculus){
                     $this->title = $this->homunculus_name;
@@ -481,24 +489,27 @@
         public function check_storage(){
             global $UID;
             global $db;
-            $s = "
+            $statement = $db->prepare("
               SELECT 1
               FROM
                 unit,
                 building
               WHERE
-                unit.id = $this->id AND
-                unit.uid = '$UID' AND
-                building.uid = '$UID' AND
+                unit.id = :id AND
+                unit.uid = :uid AND
+                building.uid = :uid AND
                 unit.building = building.id AND
-                building.building = '" . BUILDING_ID::MONSTER_STORAGE . "';
-            ";
-            $q = $db->query($s);
-            if ($q->fetchArray(SQLITE3_ASSOC) == false){
-                $this->in_storage = false;
+                building.building = :building;
+            ");
+            $statement->bindValue(':id', $this->id, SQLITE3_INTEGER);
+            $statement->bindValue(':uid', $UID, SQLITE3_INTEGER);
+            $statement->bindValue(':building', BUILDING_ID::MONSTER_STORAGE, SQLITE3_INTEGER);
+            $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
+            if ($r){
+                $this->in_storage = true;
             }
             else{
-                $this->in_storage = true;
+                $this->in_storage = false;
             }
         }
 
@@ -517,11 +528,13 @@
 
             // Runes
             $this->rune = [];
-            $s =
-              "SELECT id " .
-              "FROM rune " .
-              "WHERE assigned_to = '$this->id'; ";
-            $q = $db->query($s);
+            $statement = $db->prepare("
+              SELECT id
+              FROM rune
+              WHERE assigned_to = :assigned;
+            ");
+            $statement->bindValue(':assigned', $this->id, SQLITE3_INTEGER);
+            $q = $statement->execute();
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 array_push($this->rune, new Rune($r["id"]));
             }
@@ -538,11 +551,13 @@
             $this->rune_hp = ceil($this->rune_hp);
 
             // Artifacts
-            $s =
-              "SELECT id " .
-              "FROM artifact " .
-              "WHERE assigned_to = '$this->id'; ";
-            $q = $db->query($s);
+            $statement = $db->prepare("
+              SELECT id
+              FROM artifact
+              WHERE assigned_to = :assigned;
+            ");
+            $statement->bindValue(':assigned', $this->id, SQLITE3_INTEGER);
+            $q = $statement->execute();
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 $artifact = new Artifact($r["id"]);
                 if ($artifact->type == ARTIFACT_TYPE::ARCHETYPE){
@@ -565,7 +580,7 @@
             }
 
             // Buildings
-            $s = "
+            $statement = $db->prepare("
               SELECT
                 affected_stat,
                 bonus,
@@ -575,14 +590,21 @@
                 k_decoration_level,
                 decoration
               WHERE
-                decoration.uid = '$UID' AND
+                decoration.uid = :uid AND
                 k_decoration.id = decoration.decoration AND
                 decoration.decoration = k_decoration_level.decoration AND
                 decoration.level = k_decoration_level.level AND
-                area = " . EFFECT_AREA_ID::GENERAL . " AND
-                affected_stat IN (" . STAT_ID::ATK . ", " . STAT_ID::DEF . ", " . STAT_ID::HP . ", " . STAT_ID::SPD . ", " . STAT_ID::CRIT_DMG . ");
-            ";
-            $q = $db->query($s);
+                area = :area AND
+                affected_stat IN (:stat_atk, :stat_def, :stat_hp, :stat_spd, :stat_crd);
+            ");
+            $statement->bindValue(':uid', $UID, SQLITE3_INTEGER);
+            $statement->bindValue(':area', EFFECT_AREA_ID::GENERAL, SQLITE3_INTEGER);
+            $statement->bindValue(':stat_atk', STAT_ID::ATK, SQLITE3_INTEGER);
+            $statement->bindValue(':stat_def', STAT_ID::DEF, SQLITE3_INTEGER);
+            $statement->bindValue(':stat_hp', STAT_ID::HP, SQLITE3_INTEGER);
+            $statement->bindValue(':stat_spd', STAT_ID::SPD, SQLITE3_INTEGER);
+            $statement->bindValue(':stat_crd', STAT_ID::CRIT_DMG, SQLITE3_INTEGER);
+            $q = $statement->execute();
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 switch($r["affected_stat"]){
                     case STAT_ID::DEF:
@@ -655,17 +677,19 @@
             global $UID;
             global $db;
             $this->teams = [];
-            $s = "
+            $statement = $db->prepare("
               SELECT DISTINCT team.id AS id
               FROM
                 team,
                 team_unit
               WHERE
-                team.uid = '$UID' AND
+                team.uid = :uid AND
                 team.id = team_unit.team AND
-                team_unit.unit = '" . $this->id . "';
-            ";
-            $q = $db->query($s);
+                team_unit.unit = :unit;
+            ");
+            $statement->bindValue(':uid', $UID, SQLITE3_INTEGER);
+            $statement->bindValue(':unit', $this->id, SQLITE3_INTEGER);
+            $q = $statement->execute();
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 array_push($this->teams, new Team($r["id"], false));
             }

+ 1 - 1
application/helper/APPLICATION_Helper.php

@@ -59,7 +59,7 @@
          *   BUILDING
          * @param string|int $name Filename, with no path or extension. It can also
          * skip the padding. An id can be used.
-         * @return Image URL if found. If not, the URL to an 'unknown' icon.
+         * @return string URL if found. If not, the URL to an 'unknown' icon.
          */
         public static function img($type, $name){
             $pad = 1;

+ 24 - 90
application/helper/HTML_Helper.php

@@ -19,7 +19,7 @@
     require_once(PATH::ENTITY . "K_Decoration.php");
     require_once(PATH::ENTITY . "Rune.php");
     require_once(PATH::ENTITY . "Artifact.php");
-    require_once(PATH::ENTITY . "Rune_Craft.php");
+    require_once(PATH::ENTITY . "Enchantment.php");
     require_once(PATH::ENTITY . "Inventory.php");
 
     /**
@@ -266,7 +266,6 @@
             $html .= "<td class='grind'>\n";
             $html .= "</td>\n";
             $html .= "</tr>\n";
-            $substats = array($rune->substat_1, $rune->substat_2, $rune->substat_3, $rune->substat_4);
             $substats_name = array($rune->substat_1_name, $rune->substat_2_name, $rune->substat_3_name, $rune->substat_4_name);
             $substats_value = array($rune->substat_1_value, $rune->substat_2_value, $rune->substat_3_value, $rune->substat_4_value);
             $substats_grind = array($rune->substat_1_grind, $rune->substat_2_grind, $rune->substat_3_grind, $rune->substat_4_grind);
@@ -484,73 +483,6 @@
             }
             $html .= "</table>\n";
             $html .= "</td>\n";
-            // Details
-            /* TODO: Only quaity data, and for that the icon is enough
-            $html .= "<td class='details'>\n";
-            $html .= "<table class='table_details'>\n";
-            $html .= "<tr>\n";
-            $html .= "<td class='title'>\n";
-            $html .= "Quality:\n";
-            $html .= "</td>\n";
-            $html .= "<td class='value'>\n";
-            $html .= "<span class='quality quality_" . strtolower($artifact->quality_name) . "'>\n";
-            $html .= $artifact->quality_name . "\n";
-            $html .= "</span>\n";
-            $html .= "</td>\n";
-            $html .= "</tr>\n";
-            $html .= "<tr>\n";
-            $html .= "<td class='title'>\n";
-            $html .= "Original q.:\n";
-            $html .= "</td>\n";
-            $html .= "<td class='value'>\n";
-            $html .= "<span class='quality quality_" . strtolower($artifact->original_quality_name) . "'>\n";
-            $html .= $artifact->original_quality_name . "\n";
-            $html .= "</span>\n";
-            $html .= "</td>\n";
-            $html .= "</tr>\n";
-            $html .= "<tr>\n";
-            $html .= "<td class='title'>\n";
-            // TODO: No data on value yet
-            $html .= ""; //"Value:\n";
-            $html .= "</td>\n";
-            $html .= "<td class='value'>\n";
-            // TODO: No data on value yet
-            $html .= ""; //number_format($artifact->value, 0, ".", ",") . "\n";
-            $html .= "</td>\n";
-            $html .= "</tr>\n";
-            $html .= "<tr>\n";
-            $html .= "<td class='title'>\n";
-            // TODO: No data on efficiencies yet
-            $html .= ""; //"Efficiency:\n";
-            $html .= "</td>\n";
-            $html .= "<td class='value'>\n";
-            // TODO: No data on efficiencies yet
-            $html .= ""; //number_format($artifact->efficiency, 2, ".", ",") . "%\n";
-            $html .= "</td>\n";
-            $html .= "</tr>\n";
-            $html .= "<tr>\n";
-            $html .= "<td class='title'>\n";
-            // TODO: No data on efficiencies yet
-            $html .= ""; //"Max. eff.:\n";
-            $html .= "</td>\n";
-            $html .= "<td class='value'>\n";
-            // TODO: No data on efficiencies yet
-            $html .= ""; //number_format($artifact->max_efficiency, 2, ".", ",") . "%\n";
-            $html .= "</td>\n";
-            $html .= "</tr>\n";
-            $html .= "<tr>\n";
-            $html .= "<td class='title'>\n";
-            // TODO: No data on efficiencies yet
-            $html .= ""; //"Reached. eff.:\n";
-            $html .= "</td>\n";
-            $html .= "<td class='value'>\n";
-            // TODO: No data on efficiencies yet
-            $html .= ""; //number_format($artifact->efficiency * 100 / $artifact->max_efficiency, 2, ".", ",") . "%\n";
-            $html .= "</td>\n";
-            $html .= "</tr>\n";
-            $html .= "</table>\n";
-            */
-            $html .= "</td>\n";
             $html .= "</tr>\n";
             $html .= "</table>\n";
             return $html;
@@ -559,16 +491,16 @@
         /**
          * Generates a grindstone or gem panel.
          * 
-         * @param Rune_Craft craft Entity to get the info from.
+         * @param Enchantment Entity to get the info from.
          * @return string HTML content for a rune panel. Empty on error.
          */
-        public static function craft_panel($craft) {
-            if (!($craft instanceof Rune_Craft)){
+        public static function enchantment_panel($enchantment) {
+            if (!($enchantment instanceof Enchantment)){
                 return "";
             }
             $html = "";
-            if ($craft->gem == 1){
-                if ($craft->inmemorial == 1){
+            if ($enchantment->gem == 1){
+                if ($enchantment->inmemorial == 1){
                     $base = URL::IMG["GRIND"] . "gem_inmemorial.png";
                 }
                 else{
@@ -576,37 +508,39 @@
                 }
             }
             else{
-                if ($craft->inmemorial == 1){
+                if ($enchantment->inmemorial == 1){
                     $base = URL::IMG["GRIND"] . "grind_inmemorial.png";
                 }
                 else{
                     $base = URL::IMG["GRIND"] . "grind.png";
                 }
             }
-            if ($craft->inmemorial == 0 && isset($craft->rune)){
-                $icon = "<img class='craft_icon' src='" . $craft->rune->get_image() . "'/>\n";
+            if ($enchantment->inmemorial == 0 && isset($enchantment->rune)){
+                $icon = "<img class='enchantment_icon' src='" . $enchantment->rune->get_image() . "'/>\n";
             }
             else{
                 $icon = "";
             }
-            if (in_array($craft->stat_name, array("CRR", "CRD", "RES", "ACC", "HP%", "ATK%", "DEF%"))){
-                $stat_name = str_replace("%", "", $craft->stat_name);
-                $stat_value = "+" . $craft->min . "~" . $craft->max . "%";
+            if (in_array($enchantment->stat_name, array("CRR", "CRD", "RES", "ACC", "HP%", "ATK%", "DEF%"))){
+                $stat_name = str_replace("%", "", $enchantment->stat_name);
+                $stat_value = "+" . $enchantment->min . "~" . $enchantment->max . "%";
             }
             else{
-                $stat_name = $craft->stat_name;
-                $stat_value = "+" . $craft->min . "~" . $craft->max;
+                $stat_name = $enchantment->stat_name;
+                $stat_value = "+" . $enchantment->min . "~" . $enchantment->max;
             }
-            $html .= "<div class='craft_panel craft_quality_" . strtolower($craft->quality_name) . " rune_ancient_" . $craft->ancient . "'>\n";
-            $html .= "<img class='craft_base' src='" . $base . "'/>\n";
+            $html .= "<div class='enchantment_panel enchantment_quality_" . strtolower($enchantment->quality_name) . " enchantment_ancient_" . $enchantment->ancient . "'>\n";
+            $html .= "<img class='enchantment_base' src='" . $base . "'/>\n";
             $html .= $icon;
-            $html .= "<span class='craft_stat'>\n";
-            $html .= "<span class='craft_stat_name'>" . $stat_name . "</span>\n";
-            $html .= "<span class='craft_stat_value'>" . $stat_value . "</span>\n";
-            $html .= "</span>\n";
-            $html .= "<span class='craft_amount'>\n";
-            $html .= "x" . $craft->amount . "\n";
+            $html .= "<span class='enchantment_stat'>\n";
+            $html .= "<span class='enchantment_stat_name'>" . $stat_name . "</span>\n";
+            $html .= "<span class='enchantment_stat_value'>" . $stat_value . "</span>\n";
             $html .= "</span>\n";
+            if ($enchantment->amount > 0){
+                $html .= "<span class='enchantment_amount'>\n";
+                $html .= "x" . $enchantment->amount . "\n";
+                $html .= "</span>\n";
+            }
             $html .= "</div>\n";
             return $html;
         }

+ 0 - 1
application/page/Artifacts_Page.php

@@ -239,7 +239,6 @@
                     break;
             }
             $s = $s . " ORDER BY type, attribute, archetype, level DESC, original_quality DESC;";
-            error_log($s);
             return $s;
         }
     }

+ 17 - 0
application/page/Catalog_Page.php

@@ -26,6 +26,11 @@
          */
         public $units = [];
 
+        /**
+         * @var int[] IDs of the units marked in the catalog.
+         */
+        public $units_open = [];
+
         /**
          * @var mixed[] Default filter values.
          */
@@ -45,6 +50,7 @@
          */
         public function __construct(){
             global $db;
+            global $UID;
             $this->view = PATH::VIEW . "catalog.php";
             $this->parse_filters();
             $s_mon = $this->build_query();
@@ -52,6 +58,17 @@
             while ($r_mon = $q_mon->fetchArray(SQLITE3_ASSOC)){
                 array_push($this->units, new K_Unit($r_mon["id"], false));
             }
+            $s_open = "
+              SELECT unit
+              FROM collection
+              WHERE
+                uid = $UID AND
+                open = 1
+            ";
+            $q_open = $db->query($s_open);
+            while ($r_open = $q_open->fetchArray(SQLITE3_ASSOC)){
+                array_push($this->units_open, $r_open["unit"]);
+            }
             $this->title = "Catalog - SWDB";
             $this->description = "Catalog of all monsters";
             $this->canonical = URL::BASE . "catalog/";

+ 6 - 8
application/page/Enchantments_Page.php

@@ -13,7 +13,7 @@
      */
     require_once(PATH::PAGE . "Page.php");
     require_once(PATH::ENTITY . "Custom_Filter.php");
-    require_once(PATH::ENTITY . "Rune_Craft.php");
+    require_once(PATH::ENTITY . "Enchantment.php");
 
     /**
      * Enchantment list page model.
@@ -23,12 +23,12 @@
     class Enchantments_Page extends Page{
 
         /**
-         * @var \Rune_Craft[] Owned enchantment gems.
+         * @var \Enchantment[] Owned enchantment gems.
          */
         public $gems = [];
 
         /**
-         * @var \Rune_Craft[] Owned enchantment grindstones.
+         * @var \Enchantment[] Owned enchantment grindstones.
          */
         public $grindstones = [];
 
@@ -58,7 +58,7 @@
             }
             $q = $db->query($s);
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
-                $enchantment = new Rune_Craft($r["id"]);
+                $enchantment = new Enchantment($r["id"]);
                 //if ($enchantment->gem == true){
                 //   array_push($this->gems, $enchantment);
                 //}
@@ -162,7 +162,7 @@
             $s = "
               SELECT
                 id
-              FROM rune_craft
+              FROM enchantment
               WHERE uid = '$UID' AND (
                 $cond
               )
@@ -182,7 +182,7 @@
             global $UID;
             $s = "
               SELECT id
-              FROM rune_craft
+              FROM enchantment
               WHERE
                 uid = '$UID' AND
                 quality >= " . $this->filters["MIN_QUALITY"] . " AND
@@ -209,7 +209,6 @@
                     $s .= ") ";
                 }
             }
-            error_log( print_r($this->filters["TYPE"], TRUE) );
             if (count($this->filters["TYPE"]) == 1){
                 // Only one selectd, it's either gems or grindstones
                 if (in_array("GEM", $this->filters["TYPE"])){
@@ -282,7 +281,6 @@
             }
             $s = $s . " ORDER BY type, attribute, archetype, level DESC, original_quality DESC;";
             */
-            error_log($s);
             return $s;
         }
     }

+ 59 - 0
application/page/Error_Page.php

@@ -0,0 +1,59 @@
+<?php
+    /**
+     * Landing (login) page file.
+     *
+     * Provides a class with all the properties and methods to display the page.
+     *
+     * @category Page
+     */
+
+    /**
+     * Require dependent files if not present.
+     */
+    require_once(PATH::PAGE . "Page.php");
+
+    /**
+     * Error page model.
+     *
+     * @category Page
+     */
+    class Error_Page extends Page{
+
+        /**
+         * @var int Error code.
+         *
+         * HTTP status code.
+         */
+        public $code = 500;
+
+        /**
+         * Constructor.
+         *
+         * Retrieves the data and initializes the variables.
+         * 
+         * @param int code Error code. Optional.
+         */
+        public function __construct($code = null){
+            $this->view = PATH::VIEW . "error.php";
+            $this->title = "Error - SWDB";
+            
+            if (null == $code || intval($code) < 200 || intval($code) > 599){
+                $code = 500;
+            }
+            switch(intval($code)){
+                case 400:
+                    $this->description = "Invalid request";
+                    break;
+                case 404:
+                    $this->description = "Not found";
+                    break;
+                case 500:
+                    $this->description = "Server error";
+                    break;
+                default:
+                    $this->description = "Unknown error";
+            }
+            $this->code = intval($code);
+        }
+    }
+?>

+ 1 - 1
application/page/Fusion_Page.php

@@ -21,7 +21,7 @@
     class Fusion_Page extends Page{
 
         /**
-         * @var \K_Fusion[] List of top-level fusions to display.
+         * @var \Fusion[] List of top-level fusions to display.
          */
         public $fusion = [];
 

+ 9 - 3
application/page/Landing_Page.php

@@ -19,19 +19,25 @@
      */
     class Landing_Page extends Page{
 
+        /**
+         * @var string Error message to show when login was unsuccesfull and
+         * the user is back in the login page
+         */
+        public $error_message = "";
+
         /**
          * Constructor.
          *
          * Retrieves the data and initializes the variables.
-         *
-         ** @global resource Database connection.
          */
         public function __construct(){
-            global $db;
             $this->view = PATH::VIEW . "landing.php";
             $this->title = "Login - SWDB";
             $this->description = "Login to SWDB";
             $this->canonical = URL::BASE . "landing/";
+            if (http_response_code() == 401){
+                $this->error_message = "Invalid credentials";
+            }
         }
     }
 ?>

+ 0 - 2
application/page/Monster_Info_Page.php

@@ -41,10 +41,8 @@
          * Retrieves the data and initializes the variables.
          *
          * @param int $id Monster id. Can be any of the awakening stages
-         * @global resource Database connection.
          */
         public function __construct($id){
-            global $db;
             $this->view = PATH::VIEW . "monster_info.php";
             $title = "";
             $monster = new K_Unit($id);

+ 0 - 2
application/page/Monster_Page.php

@@ -32,10 +32,8 @@
          * Retrieves the data and initializes the variables.
          *
          * @param string $id Selected unit id
-         * @global resource Database connection.
          */
         public function __construct($id){
-            global $db;
             $this->view = PATH::VIEW . "monster.php";
             $this->unit = new Unit($id);
             $this->title = $this->unit->unit->name ." - SWDB";

+ 0 - 2
application/page/Monsters_Page.php

@@ -118,11 +118,9 @@
          *
          * @return string The query to be executed.
          * @global int Player ID.
-         * @global resource Database connection.
          */
         private function build_query(){
             global $UID;
-            global $db;
             $s = "
                 SELECT unit.id AS id
                 FROM

+ 1 - 1
application/page/Optimizer_Page.php

@@ -56,7 +56,7 @@
          * Builds the query to the rune table using the selected or default
          * filters.
          *
-         * @return The query to be executed.
+         * @return string The query to be executed.
          */
         private function build_query(){
             global $UID;

+ 49 - 50
application/page/Report_Runecraft_Page.php → application/page/Report_Rune_Enchantment_Page.php

@@ -13,14 +13,14 @@
     require_once(PATH::PAGE . "Page.php");
     require_once(PATH::ENTITY . "Unit.php");
     require_once(PATH::ENTITY . "Rune.php");
-    require_once(PATH::ENTITY . "Rune_Craft.php");
+    require_once(PATH::ENTITY . "Enchantment.php");
 
     /**
      * Upgradeable runes report page model.
      *
      * @category Page
      */
-    class Report_Runecraft_Page extends Page{
+    class Report_Rune_Enchantment_Page extends Page{
 
         /**
          * @var mixed[] List of available upgrades.
@@ -32,7 +32,7 @@
          *     upgrade = [
          *       [
          *         rune (Rune),
-         *         craft = [(Rune_Craft)]
+         *         enchant = [(Enchantment)]
          *       ]
          *     ]
          *   ]
@@ -100,7 +100,7 @@
          */
         public function __construct(){
             global $db;
-            $this->view = PATH::VIEW . "report_runecraft.php";
+            $this->view = PATH::VIEW . "report_rune_enchantment.php";
             $this->parse_filters();
             $s = $this->build_query();
             $q = $db->query($s);
@@ -124,7 +124,7 @@
                     ];
                     $rupgrade = [
                         "rune" => new Rune($r["rune"]),
-                        "craft" => [],
+                        "enchantment" => [],
                     ];
                 }
                 if ($r["rune"] != $prev_rune){
@@ -134,18 +134,18 @@
                     $prev_rune = $r["rune"];
                     $rupgrade = [
                         "rune" => new Rune($r["rune"]),
-                        "craft" => [],
+                        "enchantment" => [],
                     ];
                 }
-                $rune_craft = new Rune_craft($r["craft"]);
-                array_push($rupgrade["craft"], $rune_craft);
+                $enchantment= new Enchantment($r["enchantment"]);
+                array_push($rupgrade["enchantment"], $enchantment);
             }
             // Last entries
             array_push($upgrade["upgrade"], $rupgrade);
             array_push($this->upgrades, $upgrade);
-            $this->title = "Rune craft - SWDB";
-            $this->description = "Find runes that can be upgraded via crafting.";
-            $this->canonical = URL::BASE . "report/runecraft/";
+            $this->title = "Rune enchantment - SWDB";
+            $this->description = "Find runes that can be upgraded via enchantments.";
+            $this->canonical = URL::BASE . "report/rune_enchantment/";
         }
 
         /**
@@ -154,7 +154,6 @@
          * min_stars (1-6) and in_storage (on/off).
          */
         private function parse_filters(){
-            //$this->filters = FILTER::RUNECRAFT;
             // Monster
             if (isset($_GET["monster_min_stars"]) && intval($_GET["monster_min_stars"]) > 0 && intval($_GET["monster_min_stars"]) < 7){
                 $this->filters["MONSTER_MIN_STARS"] = $_GET["monster_min_stars"];
@@ -256,20 +255,20 @@
                 SELECT
                   unit.id AS unit,
                   rune.id AS rune,
-                  rune_craft.id AS craft
+                  enchantment.id AS enchantment
                 FROM
                   unit,
                   k_unit,
                   rune,
-                  rune_craft,
-                  k_rune_craft_type
+                  enchantment,
+                  k_enchantment_type
                 WHERE
                   unit.uid = '$UID' AND
                   rune.uid = '$UID' AND
-                  rune_craft.uid = '$UID' AND
+                  enchantment.uid = '$UID' AND
                   unit.id = rune.assigned_to AND
                   unit.unit = k_unit.id AND
-                  rune_craft.type = k_rune_craft_type.id AND
+                  enchantment.type = k_enchantment_type.id AND
                   unit.stars >= " . $this->filters["RUNE_MIN_STARS"] . " AND
                   unit.stars <= " . $this->filters["RUNE_MAX_STARS"] . " AND
                   -- RUNE FILTERS
@@ -286,10 +285,10 @@
                   rune.max_efficiency >= " . $this->filters["RUNE_MIN_MAX_EFFICIENCY"] . " AND
                   rune.max_efficiency <= " . $this->filters["RUNE_MAX_MAX_EFFICIENCY"] . " AND
                   (
-                    rune.type = rune_craft.rune OR 
-                    k_rune_craft_type.inmemorial = 1
+                    rune.type = enchantment.rune OR 
+                    k_enchantment_type.inmemorial = 1
                   ) AND
-                  k_rune_craft_type.ancient = rune.ancient AND
+                  k_enchantment_type.ancient = rune.ancient AND
                 ";
             if (strlen($this->filters["MONSTER_NAME"]) > 0){
                 $s = $s . " upper(k_unit.name) LIKE upper('%" . $this->filters["MONSTER_NAME"] . "%') AND ";
@@ -305,13 +304,13 @@
                 $s = $s . "-1) AND ";
             }
             $s .= "
-                rune_craft.stat <> rune.main_stat AND
-                rune_craft.stat <> rune.innate_stat AND
+                enchantment.stat <> rune.main_stat AND
+                enchantment.stat <> rune.innate_stat AND
                   (
-                    k_rune_craft_type.gem = 1 AND
+                    k_enchantment_type.gem = 1 AND
                     (
                       -- GEM FILTERS
-                      rune_craft.quality >= " . $this->filters["GEM_MIN_QUALITY"] . " AND
+                      enchantment.quality >= " . $this->filters["GEM_MIN_QUALITY"] . " AND
             ";
             if (count($this->filters["GEM_STAT_FROM"]) > 0){
                 $in = "(";
@@ -327,19 +326,19 @@
                     $in = $in . "$t, ";
                 }
                 $in = $in . "-1)";
-                $s .= " rune_craft.stat IN $in AND ";
+                $s .= " enchantment.stat IN $in AND ";
             }
             $s .= "
                       (
-                        rune_craft.stat <> rune.substat_1 AND
-                        rune_craft.stat <> rune.substat_2 AND
-                        rune_craft.stat <> rune.substat_3 AND
-                        rune_craft.stat <> rune.substat_4
+                        enchantment.stat <> rune.substat_1 AND
+                        enchantment.stat <> rune.substat_2 AND
+                        enchantment.stat <> rune.substat_3 AND
+                        enchantment.stat <> rune.substat_4
                       )
                     )
                   ) |
                   (
-                    k_rune_craft_type.gem = 0 AND
+                    k_enchantment_type.gem = 0 AND
                     (
                       -- GRINDSTONE FILTERS
             ";
@@ -349,34 +348,34 @@
                     $in = $in . "$t, ";
                 }
                 $in = $in . "-1)";
-                $s .= " rune_craft.stat IN $in AND ";
+                $s .= " enchantment.stat IN $in AND ";
             }
             $s .= "
-                      rune_craft.quality >= " . $this->filters["GRIND_MIN_QUALITY"] . " AND
+                      enchantment.quality >= " . $this->filters["GRIND_MIN_QUALITY"] . " AND
                       (
                         (
-                          rune_craft.stat = rune.substat_1 AND
-                          rune_craft.stat <> rune.substat_2 AND
-                          rune_craft.stat <> rune.substat_3 AND
-                          rune_craft.stat <> rune.substat_4
+                          enchantment.stat = rune.substat_1 AND
+                          enchantment.stat <> rune.substat_2 AND
+                          enchantment.stat <> rune.substat_3 AND
+                          enchantment.stat <> rune.substat_4
                         ) OR
                         (
-                          rune_craft.stat <> rune.substat_1 AND
-                          rune_craft.stat = rune.substat_2 AND
-                          rune_craft.stat <> rune.substat_3 AND
-                          rune_craft.stat <> rune.substat_4
+                          enchantment.stat <> rune.substat_1 AND
+                          enchantment.stat = rune.substat_2 AND
+                          enchantment.stat <> rune.substat_3 AND
+                          enchantment.stat <> rune.substat_4
                         ) OR
                         (
-                          rune_craft.stat <> rune.substat_1 AND
-                          rune_craft.stat <> rune.substat_2 AND
-                          rune_craft.stat = rune.substat_3 AND
-                          rune_craft.stat <> rune.substat_4
+                          enchantment.stat <> rune.substat_1 AND
+                          enchantment.stat <> rune.substat_2 AND
+                          enchantment.stat = rune.substat_3 AND
+                          enchantment.stat <> rune.substat_4
                         ) OR
                         (
-                          rune_craft.stat <> rune.substat_1 AND
-                          rune_craft.stat <> rune.substat_2 AND
-                          rune_craft.stat <> rune.substat_3 AND
-                          rune_craft.stat = rune.substat_4
+                          enchantment.stat <> rune.substat_1 AND
+                          enchantment.stat <> rune.substat_2 AND
+                          enchantment.stat <> rune.substat_3 AND
+                          enchantment.stat = rune.substat_4
                         )
                       )
                     )
@@ -392,8 +391,8 @@
                   rune.level DESC,
                   rune.id, 
                   rune.slot, 
-                  rune_craft.quality DESC,
-                  rune_craft.stat DESC;
+                  enchantment.quality DESC,
+                  enchantment.stat DESC;
                 ";
             return $s;
         }

+ 0 - 1
application/page/Report_Skillup_Page.php

@@ -273,7 +273,6 @@
                 CAST(lvl AS FLOAT) / CAST(max_lvl AS FLOAT),
                 max_lvl - lvl DESC
             ";
-            error_log($s);
             return $s;
         }
     }

+ 0 - 7
application/page/Stats_Page.php

@@ -90,7 +90,6 @@
         public function __construct(){
             global $UID;
             global $db;
-            $best_run = null;
             $this->view = PATH::VIEW . "stats.php";
             $this->title = "Stats - SWDB";
             $this->description = "Player statistics";
@@ -104,12 +103,6 @@
                 return;
             }
 
-            // TODO DEBUG Force NB10
-            /*$this->filters["AREA"] = 6001;
-            $this->filters["AREA_TYPE"] = 2;
-            $this->filters["STAGE"] = 10;*/
-            // TODO END DEBUG
-
             // Build query modifier:
             $query_where = "
               run.uid = '$UID' AND

+ 6 - 6
application/page/Teams_Page.php

@@ -49,12 +49,13 @@
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 array_push($this->teams, new Team($r["id"]));
             }
-            $s = "
+            $statement = $db->prepare("
               SELECT id
               FROM unit
-              WHERE unit.uid = '$UID';
-            ";
-            $q = $db->query($s);
+              WHERE unit.uid = :uid;
+            ");
+            $statement->bindValue(':uid', $UID, SQLITE3_INTEGER);
+            $q = $statement->execute();
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 array_push($this->units, new Unit($r["id"], false));
             }
@@ -82,10 +83,9 @@
          * Builds the query to the rune table using the selected or default
          * filters.
          *
-         * @return The query to be executed.
+         * @return string The query to be executed.
          */
         private function build_query(){
-            global $ELEMENT;
             global $UID;
             $s = "
               SELECT id

BIN
application/sw.sqlite


+ 3 - 2
application/view/artifacts.php

@@ -5,7 +5,8 @@
      * Contains the view layout and ome code to present the data.
      *
      * @category View
-     * @property_read Artifacts_Page $page The page model.
+     * @var Artifacts_Page $page The page model.
+     * @var int $UID User id.
      */
 ?>
 <!DOCTYPE html>
@@ -72,8 +73,8 @@
 ?>
                             </select>
                         </td>
-                    <tr>
                     </tr>
+                    <tr>
                         <td class='label'>
                             Type:
                         </td>

+ 9 - 3
application/view/catalog.php

@@ -5,8 +5,8 @@
      * Contains the view layout and some code to present the data
      *
      * @category View
-     * @property_read Catalog_Page $page The page model
-     * @property_read int $UID Player ID
+     * @var Catalog_Page $page The page model
+     * @var int $UID Player ID
      */
 ?>
 <!DOCTYPE html>
@@ -112,8 +112,14 @@
                             }
                             $element = $mon->element;
                         }
+                        if (!in_array($mon->id, $page->units_open)){
+                            $closed = "closed";
+                        }
+                        else{
+                            $closed = "";
+                        }
 ?>
-                        <div class='monster'>
+                        <div class='monster <?=$closed?>'>
                             <a href='/<?=$UID?>/catalog/<?=$mon->id?>'>
                                 <div class='monster_panel'>
                                     <?=HTML::unit_panel($mon)?>

+ 3 - 4
application/view/enchantments.php

@@ -5,7 +5,8 @@
      * Contains the view layout and ome code to present the data.
      *
      * @category View
-     * @property_read Artifacts_Page $page The page model.
+     * @var Artifacts_Page $page The page model.
+     * @var int $UID User id.
      */
 ?>
 <!DOCTYPE html>
@@ -220,9 +221,7 @@
                 <div id='filter_apply'>
                     <input type='submit' value='Apply'/>
                 </div>
-                </div>
             </form>
-
         </aside> <!-- #filters -->
         <main>
             <section class='content' id='enchantments'>
@@ -273,7 +272,7 @@
 <?php
                         }
 ?>
-                        <?=HTML::craft_panel($grindstone)?>
+                        <?=HTML::enchantment_panel($grindstone)?>
 <?php
                     }
 ?>

+ 45 - 0
application/view/error.php

@@ -0,0 +1,45 @@
+<?php
+    /**
+     * Error view.
+     *
+     * Contains the view layout and some code to present the data.
+     *
+     * @category View
+     * @var Landing_Page $page The page model.
+     */
+?>
+<!DOCTYPE html>
+<html lang='en'>
+    <head>
+        <meta content='text/html; charset=utf-8' http-equiv='content-type'/>
+        <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1'/>
+        <title><?=$page->title?></title>
+        <link rel='shortcut icon' href='<?=$page->favicon?>'/>
+        <!-- CSS files -->
+        <link rel='stylesheet' type='text/css' href='<?=URL::CSS?>ui.css'/>
+        <link rel='stylesheet' type='text/css' href='<?=URL::CSS?>error.css'/>
+        <!-- Meta tags -->
+        <meta name='robots' content='noindex nofollow'/>
+    </head>
+    <body>
+<?php
+        include __DIR__ . "/inc/header.php";
+?>
+        <aside>
+        </aside>
+        <main>
+            <section class='list'>
+                <h2>
+                    Error
+                </h2>
+                <article>
+                    Error <?=$page->code?>: <?=$page->description?>
+                </article>
+            </section> <!-- #catalog -->
+        </main>
+
+<?php
+        include __DIR__ . "/inc/footer.php";
+?>
+    </body>
+</html>

+ 2 - 2
application/view/fusion.php

@@ -5,8 +5,8 @@
      * Contains the view layout and some code to present the data.
      *
      * @category View
-     * @property_read Fusion_Page $page The page model.
-     * @property_read int $UID Player ID.
+     * @var Fusion_Page $page The page model.
+     * @var int $UID Player ID.
      */
 ?>
 <!DOCTYPE html>

+ 1 - 1
application/view/guild.php

@@ -5,7 +5,7 @@
      * Contains the view layout and some code to present the data.
      *
      * @category View
-     * @property_read Guild_Page $page The page model.
+     * @var Guild_Page $page The page model.
      */
 ?>
 <!DOCTYPE html>

+ 1 - 1
application/view/help.php

@@ -5,7 +5,7 @@
      * Contains the view layout and some code to present the data.
      *
      * @category View
-     * @property_read Page $page The page model.
+     * @var Page $page The page model.
      */
     // TODO: Implement
 ?>

+ 3 - 2
application/view/home.php

@@ -5,7 +5,9 @@
      * Contains the view layout and some code to present the data.
      *
      * @category View
-     * @property_read Home_Page $page The page model.
+     * @var Home_Page $page The page model.
+     * @var int $UID User id.
+     * @var int[] $EFFECT_AREA Required constants.
      */
 ?>
 <!DOCTYPE html>
@@ -484,7 +486,6 @@
 <?php
                                                     }
                                                     if ($party instanceof K_Unit){
-                                                        $enable_save = false;
                                                         $disabled = "disabled";
                                                     }
                                                     else{

+ 2 - 0
application/view/inc/header.php

@@ -6,6 +6,8 @@
      *
      * @category View
      */
+
+    /** @var int $UID User id. */
 ?>
 <header>
     <div id='logo'>

+ 12 - 1
application/view/landing.php

@@ -5,7 +5,7 @@
      * Contains the view layout and some code to present the data.
      *
      * @category View
-     * @property_read Landing_Page $page The page model.
+     * @var Landing_Page $page The page model.
      */
 ?>
 <!DOCTYPE html>
@@ -77,6 +77,17 @@
                                     <input type='submit' value='Go'/>
                                 </td>
                             </tr>
+<?php
+                            if ($page->error_message != ""){
+?>
+                                <tr>
+                                    <td class='error' colspan='2'>
+                                        <?=$page->error_message?>
+                                    </td>
+                                </tr>
+<?php
+                            }
+?>
                         </table>
                     </form>
                 </article>

+ 3 - 3
application/view/monster.php

@@ -5,8 +5,8 @@
      * Contains the view layout and some code to present the data.
      *
      * @category View
-     * @property_read Monster_Page $page The page model.
-     * @property_read int $UID Player ID.
+     * @var Monster_Page $page The page model.
+     * @var int $UID Player ID.
      */
 ?>
 <!DOCTYPE html>
@@ -66,7 +66,7 @@
 ?>
                 <div id='monster_details'>
                     <div id='exp'>
-                        <span id='exp_bar' style='width:<?=$exp_pct?>%'></span>
+                        <span id='exp_bar' style='width:<?=$exp_pct?>%'>
                         </span>
                         <span id='exp_text'>
                             <?=$exp_txt?>

+ 3 - 2
application/view/monster_info.php

@@ -5,8 +5,9 @@
      * Contains the view layout and ome code to present the data.
      *
      * @category View
-     * @property_read Monster_Info_Page The page model.
+     * @var Monster_Info_Page $page The page model.
      */
+
 ?>
 <!DOCTYPE html>
 <html lang='en'>
@@ -55,7 +56,7 @@
 ?>
                     <a href='#section_<?=$i?>'>
                         <div class='monster_panel'>
-                            <?=HTML::unit_panel($page->monster[$i])?>
+                            <?=HTML::unit_panel($monster)?>
                         </div>
                     </a>
 <?php

+ 3 - 3
application/view/monsters.php

@@ -5,8 +5,8 @@
      * Contains the view layout and ome code to present the data.
      *
      * @category View
-     * @property_read Monsters_Page $page The page model.
-     * @property_read int $UID Player ID.
+     * @var Monsters_Page $page The page model.
+     * @var int $UID Player ID.
      */
 ?>
 <!DOCTYPE html>
@@ -109,7 +109,7 @@
                             }
 ?>
                             <input type='checkbox' name='without_runes' id='filter_without_runes' <?=$checked?>/>
-                            <label for='filter_without_runes'>Monsters without runes<label>
+                            <label for='filter_without_runes'>Monsters without runes</label>
                         </td>
                     </tr>
                     <tr>

+ 2 - 2
application/view/optimizer.php

@@ -5,8 +5,8 @@
      * Contains the view layout and ome code to present the data.
      *
      * @category View
-     * @property_read Teams_Page $page The page model.
-     * @property_read int $page Player ID.
+     * @var Teams_Page $page The page model.
+     * @var int $UID User id.
      */
 ?>
 <!DOCTYPE html>

+ 3 - 11
application/view/optimizer_unit.php

@@ -5,8 +5,8 @@
      * Contains the view layout and some code to present the data.
      *
      * @category View
-     * @property_read Monster_Page $page The page model.
-     * @property_read int $UID Player ID.
+     * @var Monster_Page $page The page model.
+     * @var int $UID Player ID.
      */
 ?>
 <!DOCTYPE html>
@@ -1125,14 +1125,6 @@
                     <?=HTML::unit_panel($page->unit)?>
                 </div>
 <?php
-                if ($page->unit->experience_level != 0){
-                    $exp_pct = ceil($page->unit->exp_gained * 100 / $page->unit->experience_level);
-                    $exp_txt = $page->unit->exp_gained . " / " . $page->unit->experience_level;
-                }
-                else{
-                    $exp_pct = 100;
-                    $exp_txt = "MAX";
-                }
                 $now = time();
                 //$datediff = $now - $page->unit->create_time->getTimestamp();
                 $datediff = $now - strtotime($page->unit->create_time->format('Y-m-d H:i:s'));
@@ -1497,7 +1489,7 @@
                             </td>
                             <td class='value'>
                                 <input id='min_hp' type='range' value='<?=$page->unit->total_hp?>' min='<?=$page->unit->hp?>' max='50000' oninput='updateMinValue("hp", this);'>
-                                <input type='number' id='lbl_min_hp' value='<?=$page->unit->total_hp?>' min='<?=$page->unit->hp?>' max='50000' onChange='updateMinValue("hp", this);'></input></input><span class='slider_percent'> </span>
+                                <input type='number' id='lbl_min_hp' value='<?=$page->unit->total_hp?>' min='<?=$page->unit->hp?>' max='50000' onChange='updateMinValue("hp", this);'></input><span class='slider_percent'> </span>
                                 <a title='Remove filter' class='filter_control a_button' onclick='removeFilter("hp");'>
                                     <img alt='Remove filter' src='<?=URL::IMG["ICON"]?>nok.png'/>
                                 </a>

+ 3 - 3
application/view/profile.php

@@ -5,8 +5,8 @@
      * Contains the view layout and ome code to present the data.
      *
      * @category View
-     * @property_read Profile_Page $page The page model.
-     * @property_read int $UID Player ID.
+     * @var Profile_Page $page The page model.
+     * @var int $UID Player ID.
      */
 ?>
 <!DOCTYPE html>
@@ -277,7 +277,7 @@
                                 Visibility:
                             </td>
                             <td id='visibility'>
-                                <input type='checkbox' name='defeat' id='filter_defeat' <?=$checked?> onChange='toggleVisibility(this.value);'/>
+                                <input type='checkbox' name='visible' onChange='toggleVisibility(this.value);'/>
                                 <label for='filter_defeat'>Public profile</label>
                                 <div class='help_tooltip'>
                                     <p>

+ 3 - 2
application/view/report.php

@@ -5,7 +5,8 @@
      * Contains the view layout and ome code to present the data.
      *
      * @category View
-     * @property_read Report_Page $page The page model.
+     * @var Report_Page $page The page model.
+     * @var int $UID User id.
      */
 ?>
 <!DOCTYPE html>
@@ -75,7 +76,7 @@
                         Shows monsters with runes that can be grinded or enchanted.
                         It list the grindstones and gems that are available for each rune.
                     </p>
-                    <a class='a_button' href='/<?=$UID?>/report/runecraft/'>
+                    <a class='a_button' href='/<?=$UID?>/report/rune_enchantment/'>
                         Go
                     </a>
                 </article>

+ 10 - 9
application/view/report_runecraft.php → application/view/report_rune_enchantment.php

@@ -1,11 +1,12 @@
 <?php
     /**
-     * Rune craft report view.
+     * Rune Enchantment report view.
      *
      * Contains the view layout and ome code to present the data.
      *
      * @category View
-     * @property_read Report_Runecraft_Page $page The page model.
+     * @var Report_Rune_Enchantment_Page $page The page model.
+     * @var int $UID User id.
      */
 ?>
 <!DOCTYPE html>
@@ -17,7 +18,7 @@
         <link rel='shortcut icon' href='<?=$page->favicon?>'/>
         <!-- CSS files -->
         <link rel='stylesheet' type='text/css' href='<?=URL::CSS?>ui.css'/>
-        <link rel='stylesheet' type='text/css' href='<?=URL::CSS?>report_runecraft.css'/>
+        <link rel='stylesheet' type='text/css' href='<?=URL::CSS?>report_rune_enchantment.css'/>
         <!-- Meta tags -->
         <link rel='canonical' href='<?=$page->canonical?>'/>
         <link rel='author' href='<?=$page->author?>'/>
@@ -102,7 +103,7 @@
             <h2>
                 Report filters
             </h2>
-            <form action='/<?=$UID?>/report/runecraft/' method='get' id='filter_runecraft' class='filter'>
+            <form action='/<?=$UID?>/report/runeenchantment/' method='get' id='filter_runeenchantment' class='filter'>
                 <table>
                     <tr>
                         <td colspan='2' class='header'>
@@ -444,7 +445,7 @@
                                     }
 ?>
                                 </select>
-                            </td>
+                            </div>
                         </td>
                     </tr>
                 </table>
@@ -455,7 +456,7 @@
             <section id='results' class='content'>
                 <h2>
                     <span>
-                        <?=$upgrade["unit"]->title?>
+                        Available enchantments
                     </span>
                 </h2>
                 <table id='results'>
@@ -464,7 +465,7 @@
     ?>
                         <tr>
                             <td class='header' colspan='2'>
-                                <a href='/<?=$UID?>/monsters/<?=$unit->id?>/' target='_blank'>
+                                <a href='/<?=$UID?>/monsters/<?=$upgrade["unit"]->id?>/' target='_blank'>
                                     <div class='monster_panel'>
                                         <?=HTML::unit_panel($upgrade["unit"])?>
                                     </div>
@@ -483,9 +484,9 @@
                                 </td>
                                 <td class='upgrades'>
 <?php
-                                    foreach ($rupgrade["craft"] as $craft){
+                                    foreach ($rupgrade["enchantment"] as $enchantment){
     ?>
-                                        <?=HTML::craft_panel($craft)?>
+                                        <?=HTML::enchantment_panel($enchantment)?>
 <?php
                                     }
     ?>

+ 3 - 3
application/view/report_skillup.php

@@ -5,7 +5,8 @@
      * Contains the view layout and ome code to present the data.
      *
      * @category View
-     * @property_read Report_Skillup_Page $page The page model.
+     * @var Report_Skillup_Page $page The page model.
+     * @var int $UID User id.
      */
 ?>
 <!DOCTYPE html>
@@ -119,7 +120,6 @@
                                         Include monsters that have family members with less stars than seleced in the stars filters.
                                     </p>
                                 </div>
-                                </span>
                             </td>
                         </tr>
                         <tr>
@@ -216,7 +216,7 @@
                             case 11:
                             case 12:
                             case 13:
-                                $index_title = $index . "<span class='superindex'>th</span> - " . $unit_title;
+                                $index_title = $index . "<span class='superindex'>th</span> - " . $unit->title;
                                 break;
                         }
 ?>

+ 3 - 1
application/view/runes.php

@@ -5,7 +5,8 @@
      * Contains the view layout and ome code to present the data.
      *
      * @category View
-     * @property_read Runes_Page $page The page model.
+     * @var Runes_Page $page The page model.
+     * @var int $UID User id.
      */
 ?>
 <!DOCTYPE html>
@@ -196,6 +197,7 @@
                             <input type='number' name='max_efficiency' id='filter_efficiency_max' min='0' max='100' value='<?=$page->filters["MAX_EFFICIENCY"]?>'/>%
                         </td>
                     </tr>
+                    <tr>
                         <td class='label'>
                             Max. eff.:
                         </td>

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff