Преглед изворни кода

Merge branch 'master' into Installer

Iñigo Valentin пре 5 година
родитељ
комит
efe30a4d6b
61 измењених фајлова са 1254 додато и 172 уклоњено
  1. 8 1
      .gitignore
  2. 30 9
      application/API/v3/profile/PUT.php
  3. 88 0
      application/entity/Artifact.php
  4. 0 1
      application/entity/Artifact_Effect.php
  5. 91 5
      application/entity/Rune.php
  6. 9 0
      application/helper/HTML_Helper.php
  7. BIN
      application/sw.sqlite
  8. 114 46
      installer/recreate_key_db.sql
  9. 0 83
      parser/parse.py
  10. 913 0
      parser/swarfarm_parser
  11. 1 27
      public/css/ui.css
  12. BIN
      public/img/skill/00014901.png
  13. BIN
      public/img/skill/00014902.png
  14. BIN
      public/img/skill/00014903.png
  15. BIN
      public/img/skill/00014904.png
  16. BIN
      public/img/skill/00014905.png
  17. BIN
      public/img/skill/00014906.png
  18. BIN
      public/img/skill/00014907.png
  19. BIN
      public/img/skill/00014908.png
  20. BIN
      public/img/skill/00014909.png
  21. BIN
      public/img/skill/00014910.png
  22. BIN
      public/img/skill/00014911.png
  23. BIN
      public/img/skill/00014912.png
  24. BIN
      public/img/skill/00014913.png
  25. BIN
      public/img/skill/00014914.png
  26. BIN
      public/img/skill/00014915.png
  27. BIN
      public/img/skill/00015001.png
  28. BIN
      public/img/skill/00015002.png
  29. BIN
      public/img/skill/00015003.png
  30. BIN
      public/img/skill/00015004.png
  31. BIN
      public/img/skill/00015005.png
  32. BIN
      public/img/skill/00015006.png
  33. BIN
      public/img/skill/00015007.png
  34. BIN
      public/img/skill/00015008.png
  35. BIN
      public/img/skill/00015009.png
  36. BIN
      public/img/skill/00015010.png
  37. BIN
      public/img/skill/00015011.png
  38. BIN
      public/img/skill/00015012.png
  39. BIN
      public/img/skill/00015013.png
  40. BIN
      public/img/skill/00015014.png
  41. BIN
      public/img/skill/00015015.png
  42. BIN
      public/img/unit/00025001.png
  43. BIN
      public/img/unit/00025002.png
  44. BIN
      public/img/unit/00025003.png
  45. BIN
      public/img/unit/00025004.png
  46. BIN
      public/img/unit/00025005.png
  47. BIN
      public/img/unit/00025011.png
  48. BIN
      public/img/unit/00025012.png
  49. BIN
      public/img/unit/00025013.png
  50. BIN
      public/img/unit/00025014.png
  51. BIN
      public/img/unit/00025015.png
  52. BIN
      public/img/unit/00025101.png
  53. BIN
      public/img/unit/00025102.png
  54. BIN
      public/img/unit/00025103.png
  55. BIN
      public/img/unit/00025104.png
  56. BIN
      public/img/unit/00025105.png
  57. BIN
      public/img/unit/00025111.png
  58. BIN
      public/img/unit/00025112.png
  59. BIN
      public/img/unit/00025113.png
  60. BIN
      public/img/unit/00025114.png
  61. BIN
      public/img/unit/00025115.png

+ 8 - 1
.gitignore

@@ -1,7 +1,7 @@
 # Private files
 application/config/auth.php_*
 util/
-parser/
+parser/OLD/
 data/*.sqlite
 
 # Custom scripts
@@ -16,6 +16,12 @@ application/sw.sqlite_*
 doc/.tmp
 application/API/v2/bin/__pycache__/
 
+# Backup files
+application/sw.sqlite_*
+
+# Custom installers
+install_to_*
+
 #IDE / OS files
 .buildpath
 .kateproject.d/
@@ -32,3 +38,4 @@ ehthumbs.db
 Thumbs.db
 *.swp
 *.kate-swp
+.buildpath

+ 30 - 9
application/API/v3/profile/PUT.php

@@ -76,14 +76,29 @@
         $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
+        // I don'k know the value formula, but value only depends on quality, so... hardcoding.
+        switch ($artifact->{"natural_rank"}){
+            case QUALITY_ID::LEGEND:
+                $statement->bindValue(":value", 37660, SQLITE3_INTEGER);
+                break;
+            case QUALITY_ID::HERO:
+                $statement->bindValue(":value", 25250, SQLITE3_INTEGER);
+                break;
+            case QUALITY_ID::RARE:
+                $statement->bindValue(":value", 18461, SQLITE3_INTEGER);
+                break;
+            case QUALITY_ID::MAGIC:
+                $statement->bindValue(":value", 9330, SQLITE3_INTEGER);
+                break;
+            default:
+                $statement->bindValue(":value", 3870, SQLITE3_INTEGER);
+        }
         $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);
+        // Efficienccis will be caculated later, when instantiating the artifact.
+        $statement->bindValue(":efficiency", 0, SQLITE3_FLOAT);
+        $statement->bindValue(":max_efficiency", 0, SQLITE3_FLOAT);
         $statement->execute();
-        
+
         // Main stat
         $statement = $db->prepare("
           INSERT INTO artifact_stat (
@@ -100,7 +115,7 @@
         $statement->bindValue(":stat", $artifact->{"pri_effect"}[0], SQLITE3_INTEGER);
         $statement->bindValue(":value", $artifact->{"pri_effect"}[1], SQLITE3_INTEGER);
         $statement->execute();
-        
+
         // Base effect insert query
         $effect_query = "
           INSERT INTO artifact_effect (
@@ -121,7 +136,7 @@
             :rolls
           );
         ";
-        
+
         // Effect 1
         if (sizeof($artifact->{"sec_effects"}) > 0){
             $statement = $db->prepare($effect_query);
@@ -134,7 +149,7 @@
             $statement->bindValue(":rolls", 0, SQLITE3_INTEGER);
             $statement->execute();
         }
-        
+
         // Effect 2
         if (sizeof($artifact->{"sec_effects"}) > 1){
             $statement = $db->prepare($effect_query);
@@ -174,6 +189,9 @@
             $statement->execute();
         }
 
+        // Instantiate and calculate efficiency:
+        $instance = new Artifact($artifact->{"rid"});
+
         // Assigned?
         if (intval($artifact->{"occupied_id"}) != 0){
             $statement = $db->prepare("
@@ -352,6 +370,9 @@
             $statement->execute();
         }
 
+        // Instantiate and calculate efficiency:
+        $instance = new Rune($rune->{"rune_id"});
+
         // Assigned?
         if (intval($rune->{"occupied_id"}) != 0){
             $statement = $db->prepare("

+ 88 - 0
application/entity/Artifact.php

@@ -176,6 +176,23 @@
                 while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                     array_push($this->effects, new Artifact_Effect($this->id, $r["slot"]));
                 }
+
+                // Get efficiencies, or calculate them if not yet set.
+                if ($this->efficiency == null || $this->efficiency == "" || intval($this->efficiency) == 0){
+                    $this->efficiency = $this->calculate_efficiency();
+                    $this->max_efficiency = $this->calculate_maximum_efficiency();
+                    $statement = $db->prepare("
+                      UPDATE artifact
+                      SET
+                        efficiency = :efficiency,
+                        max_efficiency = :max_efficiency
+                      WHERE id = :artifact
+                    ");
+                    $statement->bindValue(':efficiency', $this->efficiency, SQLITE3_FLOAT);
+                    $statement->bindValue(':max_efficiency', $this->max_efficiency, SQLITE3_FLOAT);
+                    $statement->bindValue(':artifact', $this->id, SQLITE3_INTEGER);
+                    $q = $statement->execute();
+                }
             }
         }
 
@@ -202,5 +219,76 @@
             }
         }
 
+        /**
+         * Calcuates the efficiency of the artifact.
+         *
+         * @return float Efficiency
+         */
+        private function calculate_efficiency(){
+            global $db;
+            $eff = [0.0, 0.0, 0.0, 0.0];
+
+            $query = "
+              SELECT
+                min,
+                max
+              FROM
+                k_artifact_roll_type,
+                k_artifact_stat_roll
+              WHERE
+                k_artifact_roll_type.id = k_artifact_stat_roll.type AND
+                k_artifact_roll_type.initial = :initial AND
+                k_artifact_roll_type.upgrade = :upgrade AND
+                k_artifact_stat_roll.effect = :effect
+              ";
+
+            for ($i = 0; $i <= 3; $i ++){
+                if (sizeof($this->effects) > $i && $this->effects[$i] != null && $this->effects[$i]->value > 0){
+                    $value = $this->effects[$i]->value;
+                    $value = $this->effects[$i]->value;
+                    $statement = $db->prepare($query);
+                    $statement->bindValue(':initial', 1, SQLITE3_TEXT);
+                    $statement->bindValue(':upgrade', 1, SQLITE3_TEXT);
+                    $statement->bindValue(':effect', $this->effects[$i]->effect, SQLITE3_TEXT);
+                    $r_roll = $statement->execute()->fetchArray(SQLITE3_ASSOC);
+                    if ($r_roll ){
+                        $min_initial = 0;//$r_initial["min"];
+                        $max_initial = $r_roll["max"];
+                        $min_upgrade = 0;//$r_upgrade["min"];
+                        $max_upgrade = $r_roll["max"];
+                        $eff[$i] = ((5 * $value) - ( 4 * $min_upgrade + $min_initial)) / ( 8 * ($max_initial - $min_initial + (4 * ($max_upgrade + $min_upgrade))));
+                    }
+                }
+            }
+            $efficiency = 0.0;
+            for ($i = 0; $i <= 3; $i ++){
+                $efficiency += $eff[$i];
+            }
+            $efficiency *= 100;
+            return $efficiency;
+        }
+
+        /**
+         * Calcuates the maximum efficiency of the artifact.
+         *
+         * @return float Efficiency
+         */
+        private function calculate_maximum_efficiency(){
+            $max_efficiency = $this->efficiency;
+            if ($this->level < 12){
+                $max_efficiency += (1/8);
+            }
+            if ($this->level < 9){
+                $max_efficiency += (1/8);
+            }
+            if ($this->level < 6){
+                $max_efficiency += (1/8);
+            }
+            if ($this->level < 3){
+                $max_efficiency += (1/8);
+            }
+            return $max_efficiency;
+        }
+
     }
 ?>

+ 0 - 1
application/entity/Artifact_Effect.php

@@ -89,7 +89,6 @@
                 $this->value = $r["value"];
                 $this->enchant = $r["enchant"];
                 $this->grind = $r["grind"];
-                
                 $statement = $db->prepare("
                   SELECT name
                   FROM k_artifact_effect

+ 91 - 5
application/entity/Rune.php

@@ -171,8 +171,8 @@
                 $this->quality = $r["quality"];
                 $this->original_quality = $r["original_quality"];
                 $this->value = $r["value"];
-                //$this->efficiency = $r["efficiency"];
-                //$this->max_efficiency = $r["max_efficiency"];
+                $this->efficiency = $r["efficiency"];
+                $this->max_efficiency = $r["max_efficiency"];
                 $this->locked = $r["locked"];
 
                 // Get substats
@@ -201,8 +201,21 @@
                 }
                 $this->quality_name = $this->get_quality_name($this->quality);
                 $this->original_quality_name = $this->get_quality_name($this->original_quality);
-                $this->efficiency = $this->calculate_efficiency();
-                $this->max_efficiency = $this->calculate_maximum_efficiency();
+                if ($this->efficiency == null || $this->efficiency == "" || intval($this->efficiency) == 0){
+                    $this->efficiency = $this->calculate_efficiency();
+                    $this->max_efficiency = $this->calculate_maximum_efficiency();
+                    $statement = $db->prepare("
+                      UPDATE rune
+                      SET
+                        efficiency = :efficiency,
+                        max_efficiency = :max_efficiency
+                      WHERE id = :rune
+                    ");
+                    $statement->bindValue(':efficiency', $this->efficiency, SQLITE3_FLOAT);
+                    $statement->bindValue(':max_efficiency', $this->max_efficiency, SQLITE3_FLOAT);
+                    $statement->bindValue(':rune', $this->id, SQLITE3_INTEGER);
+                    $q = $statement->execute();
+                }
             }
         }
 
@@ -310,7 +323,7 @@
                 }
             }
             for ($i = 1; $i <= 4; $i ++){
-                if ($this->stats[$i] != null && $this->stats[$i]->value > 0){
+                if (sizeof($this->stats) > $i && $this->stats[$i] != null && $this->stats[$i]->value > 0){
                     $value = $this->stats[$i]->value;
                     $value = $this->stats[$i]->value;
                     $statement = $db->prepare($query);
@@ -366,5 +379,78 @@
             return $max_efficiency;
         }
 
+        /**
+         * Calcuates how many rolls each substat has.
+         *
+         * DON'T USE: Error margins are as high as 85%, which is ridiculous.
+         *
+         */
+        private function calculate_rolls(){
+            global $db;
+            $query = "
+              SELECT
+                min,
+                max
+              FROM
+                k_rune_roll_type,
+                k_rune_stat_roll
+              WHERE
+                k_rune_roll_type.id = k_rune_stat_roll.type AND
+                k_rune_roll_type.ancient = :ancient AND
+                k_rune_roll_type.initial = :initial AND
+                k_rune_roll_type.upgrade = :upgrade AND
+                k_rune_stat_roll.stars = :stars AND
+                k_rune_stat_roll.stat = :stat
+              ";
+            if ($this->innate_stat != null && $this->innate_stat->value > 0){
+                $this->innate_stat->rolls = 0;
+                $value = $this->innate_stat->value;
+                $statement = $db->prepare($query);
+                $statement->bindValue(':ancient', $this->ancient, SQLITE3_INTEGER);
+                $statement->bindValue(':initial', 1, SQLITE3_INTEGER);
+                $statement->bindValue(':upgrade', 0, SQLITE3_INTEGER);
+                $statement->bindValue(':stars', $this->stars, SQLITE3_INTEGER);
+                $statement->bindValue(':stat', $this->innate_stat->stat, SQLITE3_INTEGER);
+                $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
+                if ($r){
+                    $min_initial = 0;//$r["min"];
+                    $max_initial = $r["max"];
+                    $this->innate_stat->efficiency = 100 * (($value - $min_initial) / ($max_initial - $min_initial));
+                }
+            }
+            $max_rolls = floor(max($this->level, 12) / 3);
+            $rolls = 0;
+            for ($i = 1; $i <= 4; $i ++){
+                if ($this->stats[$i] != null && $this->stats[$i]->value > 0){
+                    $value = $this->stats[$i]->value;
+                    $statement = $db->prepare($query);
+                    $statement->bindValue(':ancient', $this->ancient, SQLITE3_INTEGER);
+                    $statement->bindValue(':initial', 1, SQLITE3_INTEGER);
+                    $statement->bindValue(':upgrade', 0, SQLITE3_INTEGER);
+                    $statement->bindValue(':stars', $this->stars, SQLITE3_INTEGER);
+                    $statement->bindValue(':stat', $this->stats[$i]->stat, SQLITE3_INTEGER);
+                    $r_initial = $statement->execute()->fetchArray(SQLITE3_ASSOC);
+                    $statement = $db->prepare($query);
+                    $statement->bindValue(':ancient', $this->ancient, SQLITE3_INTEGER);
+                    $statement->bindValue(':initial', 0, SQLITE3_INTEGER);
+                    $statement->bindValue(':upgrade', 1, SQLITE3_INTEGER);
+                    $statement->bindValue(':stars', $this->stars, SQLITE3_INTEGER);
+                    $statement->bindValue(':stat', $this->stats[$i]->stat, SQLITE3_INTEGER);
+                    $r_upgrade = $statement->execute()->fetchArray(SQLITE3_ASSOC);
+                    if ($r_initial && $r_upgrade){
+                        $min_initial = $r_initial["min"];
+                        $max_initial = $r_initial["max"];
+                        $avg_initial = ($min_initial + $max_initial) / 2;
+                        $min_upgrade = $r_upgrade["min"];
+                        $max_upgrade = $r_upgrade["max"];
+                        $avg_upgrade = ($min_upgrade + $max_upgrade) / 2;
+                        $rolls = round(max($value - $avg_initial, 0) / $avg_upgrade);
+                        $this->stats[$i]->rolls = $rolls;
+                        $this->stats[$i]->efficiency = ((5 * $value) - ( 4 * $min_upgrade + $min_initial)) / ( ($max_initial - $min_initial + (4 * ($max_upgrade + $min_upgrade))));
+                    }
+                }
+            }
+        }
+
     }
 ?>

+ 9 - 0
application/helper/HTML_Helper.php

@@ -477,6 +477,8 @@
                     $name = str_replace("Proportional", "Rel.", $name);
                     $name = str_replace("Counterattack", "Counter", $name);
                     $name = str_replace("Dealt by Attacking Together", "Attacking Together", $name);
+                    $name = str_replace("HP condition is bad", "bad HP", $name);
+                    $name = str_replace("HP condition is good", "good HP", $name);
                 }
                 $html .= $name;
                 $html .= "</td>\n";
@@ -485,6 +487,13 @@
             $html .= "</table>\n";
             $html .= "</td>\n";
             $html .= "</tr>\n";
+            $html .= "<tr>\n";
+            $html .= "<td class='details' colspan='2'>\n";
+            $html .= "EFF.: " . number_format($artifact->efficiency, 2, ".", ",") . "%&nbsp;&nbsp;\n";
+            $html .= "MX.E.: " . number_format($artifact->max_efficiency, 2, ".", ",") . "%&nbsp;&nbsp;\n";
+            $html .= "Value.: " . number_format($artifact->value, 0, ".", ",") . "\n";
+            $html .= "</td>\n";
+            $html .= "</tr>\n";
             $html .= "</table>\n";
             return $html;
         }

BIN
application/sw.sqlite


+ 114 - 46
installer/recreate_key_db.sql

@@ -621,55 +621,123 @@ CREATE TABLE k_artifact_stat(
     id INT NOT NULL PRIMARY KEY,
     name TEXT UNIQUE
 );
-INSERT INTO k_artifact_stat VALUES(100,'HP');
-INSERT INTO k_artifact_stat VALUES(101,'ATK');
-INSERT INTO k_artifact_stat VALUES(102,'DEF');
+
 CREATE TABLE k_artifact_effect(
     id INT NOT NULL PRIMARY KEY,
-    name TEXT UNIQUE
+    name TEXT UNIQUE,
+    deprecated INT NOT NULL CHECK(deprecated IN (1, 0))
 );
-INSERT INTO k_artifact_effect VALUES (200, 'ATK+ Proportional to Lost HP up to {}%');
-INSERT INTO k_artifact_effect VALUES (201, 'DEF+ Proportional to Lost HP up to {}%');
-INSERT INTO k_artifact_effect VALUES (202, 'SPD+ Proportional to Lost HP up to {}%');
-INSERT INTO k_artifact_effect VALUES (203, 'SPD Under Inability +{}%');
-INSERT INTO k_artifact_effect VALUES (204, 'ATK Increasing Effect +{}%');
-INSERT INTO k_artifact_effect VALUES (205, 'DEF Increasing Effect +{}%');
-INSERT INTO k_artifact_effect VALUES (206, 'SPD Increasing Effect +{}%');
-INSERT INTO k_artifact_effect VALUES (207, 'CRIT Rate Increasing Effect +{}%');
-INSERT INTO k_artifact_effect VALUES (208, 'Damage Dealt by Counterattack +{}%');
-INSERT INTO k_artifact_effect VALUES (209, 'Damage Dealt by Attacking Together +{}%');
-INSERT INTO k_artifact_effect VALUES (210, 'Bomb Damage +{}%');
-INSERT INTO k_artifact_effect VALUES (211, 'Damage Dealt by Reflect DMG +{}%');
-INSERT INTO k_artifact_effect VALUES (212, 'Crushing Hit DMG +{}%');
-INSERT INTO k_artifact_effect VALUES (213, 'Damage Received Under Inability -{}%');
-INSERT INTO k_artifact_effect VALUES (214, 'CRIT DMG Received -{}%');
-INSERT INTO k_artifact_effect VALUES (215, 'Life Drain +{}%');
-INSERT INTO k_artifact_effect VALUES (216, 'HP when Revived +{}%');
-INSERT INTO k_artifact_effect VALUES (217, 'Attack Bar when Revived +{}%');
-INSERT INTO k_artifact_effect VALUES (218, 'Additional Damage by {}% of HP');
-INSERT INTO k_artifact_effect VALUES (219, 'Additional Damage by {}% of ATK');
-INSERT INTO k_artifact_effect VALUES (220, 'Additional Damage by {}% of DEF');
-INSERT INTO k_artifact_effect VALUES (221, 'Additional Damage by {}% of SPD');
-INSERT INTO k_artifact_effect VALUES (300, 'Damage Dealt on Fire +{}%');
-INSERT INTO k_artifact_effect VALUES (301, 'Damage Dealt on Water +{}%');
-INSERT INTO k_artifact_effect VALUES (302, 'Damage Dealt on Wind +{}%');
-INSERT INTO k_artifact_effect VALUES (303, 'Damage Dealt on Light +{}%');
-INSERT INTO k_artifact_effect VALUES (304, 'Damage Dealt on Dark +{}%');
-INSERT INTO k_artifact_effect VALUES (305, 'Damage Received from Fire -{}%');
-INSERT INTO k_artifact_effect VALUES (306, 'Damage Received from Water -{}%');
-INSERT INTO k_artifact_effect VALUES (307, 'Damage Received from Wind -{}%');
-INSERT INTO k_artifact_effect VALUES (308, 'Damage Received from Light -{}%');
-INSERT INTO k_artifact_effect VALUES (309, 'Damage Received from Dark -{}%');
-INSERT INTO k_artifact_effect VALUES (400, '[Skill 1] CRIT DMG +{}%');
-INSERT INTO k_artifact_effect VALUES (401, '[Skill 2] CRIT DMG +{}%');
-INSERT INTO k_artifact_effect VALUES (402, '[Skill 3] CRIT DMG +{}%');
-INSERT INTO k_artifact_effect VALUES (403, '[Skill 4] CRIT DMG +{}%');
-INSERT INTO k_artifact_effect VALUES (404, '[Skill 1] Recovery +{}%');
-INSERT INTO k_artifact_effect VALUES (405, '[Skill 2] Recovery +{}%');
-INSERT INTO k_artifact_effect VALUES (406, '[Skill 3] Recovery +{}%');
-INSERT INTO k_artifact_effect VALUES (407, '[Skill 1] Accuracy +{}%');
-INSERT INTO k_artifact_effect VALUES (408, '[Skill 2] Accuracy +{}%');
-INSERT INTO k_artifact_effect VALUES (409, '[Skill 3] Accuracy +{}%');
+
+INSERT INTO k_artifact_effect VALUES (200, 'ATK+ Proportional to Lost HP up to {}%', 0);
+INSERT INTO k_artifact_effect VALUES (201, 'DEF+ Proportional to Lost HP up to {}%', 0);
+INSERT INTO k_artifact_effect VALUES (202, 'SPD+ Proportional to Lost HP up to {}%', 0);
+INSERT INTO k_artifact_effect VALUES (203, 'SPD Under Inability +{}%', 1);
+INSERT INTO k_artifact_effect VALUES (204, 'ATK Increasing Effect +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (205, 'DEF Increasing Effect +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (206, 'SPD Increasing Effect +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (207, 'CRIT Rate Increasing Effect +{}%', 1);
+INSERT INTO k_artifact_effect VALUES (208, 'Damage Dealt by Counterattack +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (209, 'Damage Dealt by Attacking Together +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (210, 'Bomb Damage +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (211, 'Damage Dealt by Reflect DMG +{}%', 1);
+INSERT INTO k_artifact_effect VALUES (212, 'Crushing Hit DMG +{}%', 1);
+INSERT INTO k_artifact_effect VALUES (213, 'Damage Received Under Inability -{}%', 1);
+INSERT INTO k_artifact_effect VALUES (214, 'CRIT DMG Received -{}%', 0);
+INSERT INTO k_artifact_effect VALUES (215, 'Life Drain +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (216, 'HP when Revived +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (217, 'Attack Bar when Revived +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (218, 'Additional Damage by {}% of HP', 0);
+INSERT INTO k_artifact_effect VALUES (219, 'Additional Damage by {}% of ATK', 0);
+INSERT INTO k_artifact_effect VALUES (220, 'Additional Damage by {}% of DEF', 0);
+INSERT INTO k_artifact_effect VALUES (221, 'Additional Damage by {}% of SPD', 0);
+INSERT INTO k_artifact_effect VALUES (222, "CRIT DMG + up to {}% as the enemy's HP condition is good", 0);
+INSERT INTO k_artifact_effect VALUES (223, "CRIT DMG + up to {}% as the enemy's HP condition is bad", 0);
+INSERT INTO k_artifact_effect VALUES (224, 'Single-target skill CRIT DMG +{}% on your turn', 0);
+INSERT INTO k_artifact_effect VALUES (300, 'Damage Dealt on Fire +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (301, 'Damage Dealt on Water +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (302, 'Damage Dealt on Wind +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (303, 'Damage Dealt on Light +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (304, 'Damage Dealt on Dark +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (305, 'Damage Received from Fire -{}%', 0);
+INSERT INTO k_artifact_effect VALUES (306, 'Damage Received from Water -{}%', 0);
+INSERT INTO k_artifact_effect VALUES (307, 'Damage Received from Wind -{}%', 0);
+INSERT INTO k_artifact_effect VALUES (308, 'Damage Received from Light -{}%', 0);
+INSERT INTO k_artifact_effect VALUES (309, 'Damage Received from Dark -{}%', 0);
+INSERT INTO k_artifact_effect VALUES (400, '[Skill 1] CRIT DMG +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (401, '[Skill 2] CRIT DMG +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (402, '[Skill 3] CRIT DMG +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (403, '[Skill 4] CRIT DMG +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (404, '[Skill 1] Recovery +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (405, '[Skill 2] Recovery +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (406, '[Skill 3] Recovery +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (407, '[Skill 1] Accuracy +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (408, '[Skill 2] Accuracy +{}%', 0);
+INSERT INTO k_artifact_effect VALUES (409, '[Skill 3] Accuracy +{}%', 0);
+
+CREATE TABLE k_artifact_roll_type(
+    id INT NOT NULL PRIMARY KEY,
+    ancient INT NOT NULL CHECK(ancient IN (1, 0)),
+    initial INT NOT NULL CHECK(initial IN (1, 0)),
+    upgrade INT NOT NULL CHECK(upgrade IN (1, 0)),
+    description TEXT
+);
+
+INSERT INTO k_artifact_roll_type VALUES(1, 0, 1, 1, 'Initial and upgrade for artifacts');
+CREATE TABLE k_artifact_stat_roll(
+    type INT NOT NULL REFERENCES k_artifact_roll_type(id),
+    effect INT NOT NULL REFERENCES k_artifact_effect(id),
+    min FLOAT NOT NULL,
+    max FLOAT NOT NULL,
+    PRIMARY KEY(type, effect)
+);
+INSERT INTO k_artifact_stat_roll VALUES (1, 200, 9, 14); -- ATK+ Proportional to Lost HP
+INSERT INTO k_artifact_stat_roll VALUES (1, 201, 9, 14); -- DEF+ Proportional to Lost HP
+INSERT INTO k_artifact_stat_roll VALUES (1, 202, 9, 14); -- SPD+ Proportional to Lost HP
+INSERT INTO k_artifact_stat_roll VALUES (1, 204, 3, 5); -- ATK Increasing Effect
+INSERT INTO k_artifact_stat_roll VALUES (1, 205, 2, 4); -- DEF Increasing Effect
+INSERT INTO k_artifact_stat_roll VALUES (1, 206, 4, 6); -- SPD Increasing Effect
+INSERT INTO k_artifact_stat_roll VALUES (1, 207, 3, 6); -- Crit Rate Increasing Effect
+INSERT INTO k_artifact_stat_roll VALUES (1, 208, 2, 4); -- Damage Dealt by Counterattack
+INSERT INTO k_artifact_stat_roll VALUES (1, 209, 2, 4); -- Damage Dealt by Attacking Together
+INSERT INTO k_artifact_stat_roll VALUES (1, 210, 2, 4); -- Bomb damage
+INSERT INTO k_artifact_stat_roll VALUES (1, 211, 1, 3); -- Damage Dealt by Reflect DMG
+INSERT INTO k_artifact_stat_roll VALUES (1, 212, 2, 4); -- Crushing Hit DMG
+INSERT INTO k_artifact_stat_roll VALUES (1, 213, 1, 3); -- Damage Received Under Inability
+INSERT INTO k_artifact_stat_roll VALUES (1, 214, 2, 4); -- CRIT DMG Received
+INSERT INTO k_artifact_stat_roll VALUES (1, 215, 5, 8); -- Life Drain
+INSERT INTO k_artifact_stat_roll VALUES (1, 216, 4, 6); -- HP when Revived
+INSERT INTO k_artifact_stat_roll VALUES (1, 217, 4, 6); -- Attack Bar when Revived
+INSERT INTO k_artifact_stat_roll VALUES (1, 218, 0.2, 0.3); -- Additional Damage by {}% of HP
+INSERT INTO k_artifact_stat_roll VALUES (1, 219, 2, 4); -- Additional Damage by {}% of ATK
+INSERT INTO k_artifact_stat_roll VALUES (1, 220, 2, 4); -- Additional Damage by {}% of DEF
+INSERT INTO k_artifact_stat_roll VALUES (1, 221, 25, 40); -- Additional Damage by {}% of SPD
+INSERT INTO k_artifact_stat_roll VALUES (1, 222, 4, 6); -- CRIT DMG + as the enemys condtion is good
+INSERT INTO k_artifact_stat_roll VALUES (1, 223, 8, 12); -- CRIT DMG + as the enemys condtion is bad
+INSERT INTO k_artifact_stat_roll VALUES (1, 224, 2, 4); -- Single-target skill CRIT DMG +{}% on your turn
+INSERT INTO k_artifact_stat_roll VALUES (1, 300, 3, 5); --Damage Dealt on Fire
+INSERT INTO k_artifact_stat_roll VALUES (1, 301, 3, 5); -- Damage Dealt on Water
+INSERT INTO k_artifact_stat_roll VALUES (1, 302, 3, 5); -- Damage Dealt on Wind
+INSERT INTO k_artifact_stat_roll VALUES (1, 303, 3, 5); -- Damage Dealt on Light
+INSERT INTO k_artifact_stat_roll VALUES (1, 304, 3, 5); -- Damage Dealt on Dark
+INSERT INTO k_artifact_stat_roll VALUES (1, 305, 4, 6); -- Damage Received from Fire
+INSERT INTO k_artifact_stat_roll VALUES (1, 306, 4, 6); -- Damage Received from Water
+INSERT INTO k_artifact_stat_roll VALUES (1, 307, 4, 6); -- Damage Received from Wind
+INSERT INTO k_artifact_stat_roll VALUES (1, 308, 4, 6); -- Damage Received from Light
+INSERT INTO k_artifact_stat_roll VALUES (1, 309, 4, 6); -- Damage Received from Dark
+INSERT INTO k_artifact_stat_roll VALUES (1, 400, 4, 6); -- [Skill 1] CRIT DMG
+INSERT INTO k_artifact_stat_roll VALUES (1, 401, 4, 6); -- [Skill 2] CRIT DMG
+INSERT INTO k_artifact_stat_roll VALUES (1, 402, 4, 6); -- [Skill 3] CRIT DMG
+INSERT INTO k_artifact_stat_roll VALUES (1, 403, 4, 6); -- [Skill 4] CRIT DMG
+INSERT INTO k_artifact_stat_roll VALUES (1, 404, 4, 6); -- [Skill 1] Recovery
+INSERT INTO k_artifact_stat_roll VALUES (1, 405, 4, 6); -- [Skill 2] Recovery
+INSERT INTO k_artifact_stat_roll VALUES (1, 406, 4, 6); -- [Skill 3] Recovery
+INSERT INTO k_artifact_stat_roll VALUES (1, 407, 4, 6); -- [Skill 1] Accuracy
+INSERT INTO k_artifact_stat_roll VALUES (1, 408, 4, 6); -- [Skill 2] Accuracy
+INSERT INTO k_artifact_stat_roll VALUES (1, 409, 4, 6); -- [Skill 3] Accuracy
+
+INSERT INTO k_artifact_stat VALUES(100,'HP');
+INSERT INTO k_artifact_stat VALUES(101,'ATK');
+INSERT INTO k_artifact_stat VALUES(102,'DEF');
 CREATE TABLE k_enchantment(
     id INT NOT NULL PRIMARY KEY,
     name TEXT UNIQUE

+ 0 - 83
parser/parse.py

@@ -1,83 +0,0 @@
-#!/usr/bin/python3
-
-import urllib3
-import sys
-import os
-import requests
-
-
-urls = [
-  'http://summonerswar.wikia.com/wiki/Fire_Monsters',
-  'http://summonerswar.wikia.com/wiki/Water_Monsters',
-  'http://summonerswar.wikia.com/wiki/Wind_Monsters',
-  'http://summonerswar.wikia.com/wiki/Light_Monsters',
-  'http://summonerswar.wikia.com/wiki/Dark_Monsters'
-]
-http = urllib3.PoolManager()
-fsql = open('data.sql', 'w')
-fget = open('img.sh', 'w')
-id = 1;
-
-for url in urls:
-    page = http.request('GET', url).data.decode('utf-8')
-    page = page[page.index('</th></tr>'):]
-    element = url[url.rfind('/') + 1:url.rfind('_')]
-
-    while '</td></tr>' in page:
-        
-        murl = 'http://summonerswar.wikia.com/' + page[page.index('a href="') + 8:page.index('"', page.index('a href="') + 8)]
-        print(murl)
-        page = page[page.index('</td></tr>') + 1:]
-
-        '''page = page[page.index('<td style="text-align:center;">') + 1:]
-        monster = page[:page.index('</tr>') + 5]
-        name = monster[monster.index('title="') + 7:monster.index(' (', monster.index('title="') + 8)]
-        monster = monster[monster.index('"', monster.index('title="') + 8):]
-        img = monster[monster.index('data-src="') + 10:monster.index('"', monster.index('data-src="') + 11)]
-        monster = monster[monster.index('<td style="text-align:center;">') + 1:]
-        monster = monster[monster.index('<td style="text-align:center;">') + 1:]
-        tpe = monster[monster.index('>') + 1:monster.index('\n')]
-        monster = monster[monster.index('<td style="text-align:center;">') + 1:]
-        monster = monster[monster.index('<td style="text-align:center;">') + 1:]
-        hp = monster[monster.index('>') + 1:monster.index('\n')]
-        monster = monster[monster.index('<td style="text-align:center;">') + 1:]
-        atk = monster[monster.index('>') + 1:monster.index('\n')]
-        monster = monster[monster.index('<td style="text-align:center;">') + 1:]
-        df = monster[monster.index('>') + 1:monster.index('\n')]
-        monster = monster[monster.index('<td style="text-align:center;">') + 1:]
-        spd = monster[monster.index('>') + 1:monster.index('\n')]
-        print("NAME:    " + name)
-        print("ELEMENT: " + element)
-        print("IMG:     " + img)
-        print("TYPE:    " + tpe)
-        print("HP:      " + hp)
-        print("ATK:     " + atk)
-        print("DEF:     " + df)
-        print("SPD:     " + spd)
-        sys.exit("DEBUG EXIT")
-
-        
-
-        element = monster[monster.index('"element">') + 10:monster.index('<', monster.index('"element">'))]
-        print("ELEMENT: " + element)
-        monster = monster[monster.index('<', monster.index('"element">')):]
-
-        name = monster[monster.index('<h3>') + 4:monster.index('</h3>')]
-        print("NAME: " + name)
-        monster = monster[monster.index('</h3>'):]
-
-        img_aw = monster[monster.index('data-src="', monster.index('icon')) + 10:monster.index('"', monster.index('data-src="', monster.index('icon')) + 11)]
-        print("IMAGE (AW): " + img_aw)
-        monster = monster[monster.index('"', monster.index('data-src="', monster.index('icon')) + 11):]
-
-        name_aw = monster[monster.index('>', monster.index('"name"')) + 1:monster.index('<', monster.index('"name"'))]
-        print("NAME (AW): " + name_aw)
-        monster = monster[monster.index('<', monster.index('"name"')):]
-
-        print("wget " + img + " -O monster/" + "{:04d}".format(id) + ".png", file=fget)
-        print("wget " + img_aw + " -O monster/awaken/" + "{:04d}".format(id) + ".png", file=fget)
-        print("INSERT INTO monster (id, name, name_aw, element, stars, base) VALUES (" + str(id) + ", '" + name + "', '" + name_aw + "', '" + element + "', " + str(star) + ", null);", file=fsql)
-
-        id = id + 1
-        #sys.exit("DEBUG EXIT")
-        '''

+ 913 - 0
parser/swarfarm_parser

@@ -0,0 +1,913 @@
+#!/bin/php
+<?php
+
+    /**
+     * Swarfarm URL of the monster list (first page).
+     */
+    CONST URL_UNIT = 'https://swarfarm.com/api/v2/monsters/';
+
+    /**
+     * Swarfarm URL of the skill list (first page).
+     */
+    CONST URL_SKILL = 'https://swarfarm.com/api/v2/skills/';
+
+    /**
+     * Path to the application
+     */
+    CONST PATH = __DIR__ . "/../";
+
+    /*
+     * Script verbosity
+     *
+     * 0: Critical
+     * 1: Error
+     * 2: Warning
+     * 3: Info
+     * 4: Debug
+     * 5: Trace
+     */
+    $args = array(
+        "verbosity" => 3,
+        "override_images" => false,
+        "skip_downloads" => false,
+        "only_downloads" => false,
+        "only_new" => false
+    );
+
+    /**
+     * Echoes an output message to stdout, depending on the verbosity level.
+     *
+     * @param int $level Message priority.
+     * @param string $message content.
+     */
+    function output($level, $message, $keep_format = false){
+        global $args;
+        if ($level <= $args["verbosity"]){
+            if ($keep_format == false){
+                $message = str_replace("\n", " ", $message);
+                while (strpos($message, "  ") !== false){
+                    $message = str_replace("  ", " ", $message);
+                }
+            }
+            if (substr($message, -1) != PHP_EOL && $keep_format == false){
+                $message .= PHP_EOL;
+            }
+            echo $message;
+        }
+    }
+
+    /**
+     * Downloads a file.
+     *
+     * @param string $url The file to download.
+     * @param string $target File destination.
+     * @return boolean true on success, false on error.
+     */
+    function download($url, $target){
+        global $args;
+        if ($args["skip_downloads"] == true){
+            return true;
+        }
+        if (!file_exists($target) || $args["override_images"] == true){
+            output(4, "Downloading file '$url' to: '$target'");
+            if (!file_put_contents($target, file_get_contents($url))) { 
+                output(1, "Error downloading file '$url' to: '$target'");
+                return false;
+            }
+            else{
+                return true;
+            }
+        }
+        else{
+            return true;
+        }
+    }
+
+    /**
+     * Gets an entity com2us_id from it's Swarfarm id.
+     *
+     * @param int $swarfarm_id Entity's Swarfarm ID.
+     * @return int Entity's com2us_id, -1 on error.
+     */
+    function get_com2us_id($swarfarm_id){
+        if ($swarfarm_id == null){
+            return null;
+        }
+        $contents = file_get_contents(URL_UNIT . $swarfarm_id . "/");
+        if($contents === false){
+            //Print out the contents.
+            output(1, "Unable to retrieve monster.");
+            return -1;
+        }
+        $contents = json_decode($contents);
+        return $contents->com2us_id;
+    }
+
+    /**
+     * Gets an element ID from it's name
+     *
+     * @param string $name Element name (case insensitive).
+     * @return int Element ID, -1 on unknown element.
+     */
+    function get_element_id($name){
+        $id = -1;
+        switch (strtoupper($name)){
+            case "MAGIC":
+                $id = 0;
+                break;
+            case "WATER":
+                $id = 1;
+                break;
+            case "FIRE":
+                $id = 2;
+                break;
+            case "WIND":
+                $id = 3;
+                break;
+            case "LIGHT":
+                $id = 4;
+                break;
+            case "DARK":
+                $id = 5;
+                break;
+            case "PURE":
+                $id = 6;
+                break;
+        }
+        return $id;
+    }
+
+    /**
+     * Gets an archetype ID from it's name
+     *
+     * @param string $name Archetype name (case insensitive).
+     * @return int Archetype ID, -1 on unknown archetype.
+     */
+    function get_archetype_id($name){
+        $id = 0; // NONE
+        switch (strtoupper($name)){
+            case "ATTACK":
+                $id = 1;
+                break;
+            case "DEFENSE":
+                $id = 2;
+                break;
+            case "HP":
+                $id = 3;
+                break;
+            case "SUPPORT":
+                $id = 4;
+                break;
+            case "MATERIAL":
+                $id = 5;
+                break;
+        }
+        return $id;
+    }
+
+    /**
+     * Prints the help message.
+     */
+    function show_help(){
+        echo "\nSwarfarm parser\n\n";
+        echo "Refreshes data using the Swarfarm API.\n\n";
+        echo "Usage:\n\n";
+        echo "    swarfarm_parser <options...>\n\n";
+        echo "Options:\n\n";
+        echo " +--------+------------------+---------------------------------------------------+\n";
+        echo " | -s     | --skip-downloads |  Don't download images.                           |\n";
+        echo " +--------+------------------+---------------------------------------------------+\n";
+        echo " | -o     | --only-downloads |  Download images, but don't make changes to       |\n";
+        echo " |        |                  |  the database.                                    |\n";
+        echo " +--------+------------------+---------------------------------------------------+\n";
+        echo " | -n     | --only-new       |  Look for new units or skills, but don't update   |\n";
+        echo " |        |                  |  existing ones.                                   |\n";
+        echo " +--------+------------------+---------------------------------------------------+\n";
+        echo " | -v [n] | --verbosity [n]  |  Controls the script output to the console.       |\n";
+        echo " |        |                  |  Values for [n]:                                  |\n";
+        echo " |        |                  |    0: Only show critical errors.                  |\n";
+        echo " |        |                  |    1: Only error messages.                        |\n";
+        echo " |        |                  |    2: Only error and warning messages.            |\n";
+        echo " |        |                  |    3: Standard output.                            |\n";
+        echo " |        |                  |    4: Include debug messages.                     |\n";
+        echo " |        |                  |    5: Print traces (lots of lines!).              |\n";
+        echo " +--------+------------------+---------------------------------------------------+\n";
+    }
+
+    /**
+     * Establishes a connection with the database.
+     *
+     * @param string $name Archetype name (case insensitive).
+     * @return resource Database connection
+     */
+    function connect_to_database(){
+        // Connect to the database
+        $db = new SQLite3(PATH . "application/sw.sqlite");
+        if (!$db) {
+            output(0, "Error connecting to th database: " . $db->lastErrorMsg());
+            exit();
+        }
+        // Disable integrity
+        $db->query("PRAGMA foreign_keys=OFF;");
+        output(4, "Connected to databse: " . PATH . "application/sw.sqlite");
+        return $db;
+    }
+
+    // Parse arguments
+    $accepted = array(
+      "--verbosity", "-v",
+      "--skip-downloads", "-s",
+      "--only-downloads", "-d",
+      "--only-new", "-n",
+      "--override-images", "-o",
+      "--help", "-h"
+    );
+    for ($i = 1; $i < count($argv); $i++){
+        if ($argv[$i] == "--help" || $argv[$i] == "-h"){
+            show_help();
+            return 0;
+        }
+        elseif (!in_array($argv[$i], $accepted)){
+            echo "Unrecognized option " . $argv[$i] . "\n";
+            return -1;
+        }
+        elseif ($argv[$i] == "--verbosity" || $argv[$i] == "-v"){
+            if (count($argv) >= $i + 2 && is_int($argv[$i + 1]) && intval($argv[$i + 1]) >= 0 && intval($argv[$i + 1]) <= 5){
+                $args["verbosity"] = intval($argv[$i + 1]);
+                $i ++;
+            }
+            else{
+                echo "Unrecognized value for parameter " . $argv[$i] . ": " . $argv[$i + 1] . "\n";
+                echo "Accepted values are:\n";
+                echo "  0 (critical)\n";
+                echo "  1 (error)\n";
+                echo "  2 (warning)\n";
+                echo "  3 (info)\n";
+                echo "  4 (debug)\n";
+                echo "  5 (trace)\n";
+            }
+        }
+        elseif ($argv[$i] == "--skip-downloads" || $argv[$i] == "-s"){
+            if (in_array("-d", $argv)){
+                echo "Conflicting arguments: '-d' and '" . $argv[$i] . "'\n";
+                return -1;
+            }
+            elseif (in_array("--only-downloads", $argv)){
+                echo "Conflicting arguments: '--only-downloads' and '" . $argv[$i] . "'\n";
+                return -1;
+            }
+            else{
+                $args["skip_downloads"] = true;
+            }
+        }
+        elseif ($argv[$i] == "--only-downloads" || $argv[$i] == "-s"){
+            if (in_array("-s", $argv)){
+                echo "Conflicting arguments: '-s' and '" . $argv[$i] . "'\n";
+                return -1;
+            }
+            elseif (in_array("--skip-downloads", $argv)){
+                echo "Conflicting arguments: '--skip-downloads' and '" . $argv[$i] . "'\n";
+                return -1;
+            }
+            else{
+                $args["only_downloads"] = true;
+            }
+        }
+        elseif ($argv[$i] == "--only-new" || $argv[$i] == "-n"){
+            $args["only_new"] = true;
+        }
+        elseif ($argv[$i] == "--override-images" || $argv[$i] == "-o"){
+            if (in_array("-s", $argv)){
+                echo "Conflicting arguments: '-s' and '" . $argv[$i] . "'\n";
+                return -1;
+            }
+            elseif (in_array("--skip-downloads", $argv)){
+                echo "Conflicting arguments: '--skip-downloads' and '" . $argv[$i] . "'\n";
+                return -1;
+            }
+            else{
+                $args["override_images"] = true;
+            }
+        }
+    }
+    output(4, "Resolved arguments:");
+    foreach($args as $key => $value) {
+        output(4, "  $key => " . var_export($value, true));
+    }
+
+    // Connect to the database
+    $db = connect_to_database();
+
+    // Loop skills pages
+    $next = URL_SKILL;
+    while ($next != null){
+        output(3, "Reading skills page: $next");
+        $contents = file_get_contents($next);
+        if ($contents === false){
+            output(0, "Unable to read skill page: $next");
+            return -1;
+        }
+        $contents = json_decode($contents);
+        $next = $contents->next;
+        $list = $contents->results;
+        foreach ($list as $skill){
+            $statement = $db->prepare("SELECT * FROM k_skill WHERE id = :id");
+            $statement->bindValue(":id", $skill->com2us_id, SQLITE3_INTEGER);
+            $q = $statement->execute();
+            $r = $q->fetchArray(SQLITE3_ASSOC);
+            if (!$r){
+                // New skill, insert directly
+                $statement = $db->prepare("
+                  INSERT INTO k_skill(
+                    id,
+                    name,
+                    description,
+                    slot,
+                    cooltime,
+                    hits,
+                    passive,
+                    aoe,
+                    max_level,
+                    multiplier_formula
+                  ) VALUES (
+                    :id,
+                    :name,
+                    :description,
+                    :slot,
+                    :cooltime,
+                    :hits,
+                    :passive,
+                    :aoe,
+                    :max_level,
+                    :multiplier_formula
+                  );
+                ");
+                $statement->bindValue(":id", $skill->com2us_id, SQLITE3_INTEGER);
+                $statement->bindValue(":name", $skill->name, SQLITE3_TEXT);
+                $statement->bindValue(":description", $skill->name, SQLITE3_TEXT);
+                $statement->bindValue(":slot", $skill->slot, SQLITE3_TEXT);
+                $statement->bindValue(":cooltime", $skill->cooltime, SQLITE3_INTEGER);
+                $statement->bindValue(":hits", $skill->hits, SQLITE3_INTEGER);
+                $statement->bindValue(":passive", $skill->passive, SQLITE3_INTEGER);
+                $statement->bindValue(":aoe", $skill->aoe, SQLITE3_INTEGER);
+                $statement->bindValue(":max_level", $skill->max_level, SQLITE3_INTEGER);
+                $statement->bindValue(":multiplier_formula", $skill->multiplier_formula, SQLITE3_TEXT);
+                output(4, "QUERY: " . $statement->getSQL(true));
+                $statement->execute();
+                $url = 'https://swarfarm.com/static/herders/images/skills/' . $skill->icon_filename;
+                $out = PATH . "public/img/skill/" . sprintf('%08d', $skill->com2us_id) . ".png";
+                download($url, $out);
+                $level = 1;
+                $statement = $db->prepare("INSERT INTO k_skill_level (skill, level, description) VALUES (:skill, :level, :description);");
+                $statement->bindValue(":skill", $skill->com2us_id, SQLITE3_INTEGER);
+                $statement->bindValue(":level", $level, SQLITE3_INTEGER);
+                foreach ($skill->upgrades as $upgrade){
+                    $level ++;
+                    $statement = $db->prepare("INSERT INTO k_skill_level (skill, level, description) VALUES (:skill, :level, :description);");
+                    $statement->bindValue(":skill", $skill->com2us_id, SQLITE3_INTEGER);
+                    $statement->bindValue(":level", $level, SQLITE3_INTEGER);
+                    $statement->bindValue(":description", str_replace("{0}", $upgrade->amount, $upgrade->effect), SQLITE3_TEXT);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                foreach ($skill->effects as $effect){
+                    $statement = $db->prepare("INSERT INTO k_skill_effect (skill, effect) VALUES (:skill, :effect);");
+                    $statement->bindValue(":skill", $skill->com2us_id, SQLITE3_INTEGER);
+                    $statement->bindValue(":effect", $effect->effect->id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+            }
+            else{
+                // Existing skill, compare all values
+                if ($r["name"] != $skill->name){
+                    $statement = $db->prepare("UPDATE k_skill SET name = :name WHERE id = :id;");
+                    $statement->bindValue(":name", $skill->name, SQLITE3_TEXT);
+                    $statement->bindValue(":id", $skill->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["description"] != $skill->description){
+                    $statement = $db->prepare("UPDATE k_skill SET description = :description WHERE id = :id;");
+                    $statement->bindValue(":description", $skill->description, SQLITE3_TEXT);
+                    $statement->bindValue(":id", $skill->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["slot"] != $skill->slot){
+                    $statement = $db->prepare("UPDATE k_skill SET slot = :slot WHERE id = :id;");
+                    $statement->bindValue(":slot", $skill->slot, SQLITE3_TEXT);
+                    $statement->bindValue(":id", $skill->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["cooltime"] != $skill->cooltime){
+                    $statement = $db->prepare("UPDATE k_skill SET cooltime = :cooltime WHERE id = :id;");
+                    $statement->bindValue(":cooltime", $skill->cooltime, SQLITE3_TEXT);
+                    $statement->bindValue(":id", $skill->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["hits"] != $skill->hits){
+                    $statement = $db->prepare("UPDATE k_skill SET hits = :hits WHERE id = :id;");
+                    $statement->bindValue(":hits", $skill->hits, SQLITE3_TEXT);
+                    $statement->bindValue(":id", $skill->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                $passive = ($skill->passive == 1) ? (1) : (0);
+                if ($r["passive"] != $passive){
+                    $statement = $db->prepare("UPDATE k_skill SET passive = :passive WHERE id = :id;");
+                    $statement->bindValue(":passive", $passive, SQLITE3_TEXT);
+                    $statement->bindValue(":id", $skill->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                $aoe = ($skill->aoe == 1) ? (1) : (0);
+                if ($r["aoe"] != $aoe){
+                    $statement = $db->prepare("UPDATE k_skill SET aoe = :aoe WHERE id = :id;");
+                    $statement->bindValue(":aoe", $aoe, SQLITE3_TEXT);
+                    $statement->bindValue(":id", $skill->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["max_level"] != $skill->max_level){
+                    $statement = $db->prepare("UPDATE k_skill SET max_level = :max_level WHERE id = :id;");
+                    $statement->bindValue(":max_level", $skill->max_level, SQLITE3_TEXT);
+                    $statement->bindValue(":id", $skill->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["multiplier_formula"] != $skill->multiplier_formula){
+                    $statement = $db->prepare("UPDATE k_skill SET multiplier_formula = :multiplier_formula WHERE id = :id;");
+                    $statement->bindValue(":multiplier_formula", $skill->multiplier_formula, SQLITE3_TEXT);
+                    $statement->bindValue(":id", $skill->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+
+                // Compare existing effects
+                $statement = $db->prepare("SELECT count(effect) AS c FROM k_skill_effect WHERE skill = :skill;");
+                $statement->bindValue(":skill", $skill->com2us_id, SQLITE3_INTEGER);
+                $q = $statement->execute();
+                $r = $q->fetchArray(SQLITE3_ASSOC);
+                if ($r["c"] != sizeof($skill->effects)){
+                    $statement = $db->prepare("DELETE FROM k_skill_effect WHERE skill = :skill;");
+                    $statement->bindValue(":skill", $skill->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                    foreach ($skill->effects as $effect){
+                        $statement = $db->prepare("INSERT INTO k_skill_effect (skill, effect) VALUES (:skill, :effect);");
+                        $statement->bindValue(":skill", $skill->com2us_id, SQLITE3_INTEGER);
+                        $statement->bindValue(":effect", $effect->effect->id, SQLITE3_INTEGER);
+                        output(4, "QUERY: " . $statement->getSQL(true));
+                        $statement->execute();
+                    }
+                }
+
+                // Image download
+                $out = PATH . "public/img/skill/" . sprintf('%08d', $skill->com2us_id) . ".png";
+                $url = 'https://swarfarm.com/static/herders/images/skills/' . $skill->icon_filename;   
+                download($url, $out);
+            }
+        }
+        //echo "NEXTPAGE: " . $next;
+    }
+
+    // Loop units pages
+    $next = URL_UNIT;
+    while ($next != null){
+        output(3, "Reading units page: $next");
+        $contents = file_get_contents($next);
+        if($contents === false){
+            output(0, "Unable to retrieve monster list.");
+            return -1;
+        }
+        $contents = json_decode($contents);
+        $next = $contents->next;
+        $list = $contents->results;
+        foreach ($list as $unit){
+            $statement = $db->prepare("SELECT * FROM k_unit WHERE id = :id");
+            $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+            $q = $statement->execute();
+            $r = $q->fetchArray(SQLITE3_ASSOC);
+            if (!$r){
+                $statement = $db->prepare("
+                  INSERT INTO k_unit(
+                    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
+                  ) VALUES (
+                    :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
+                  );
+                ");
+                $statement->bindValue(":id", $unit->com2us_id, SQLITE3_TEXT);
+                $statement->bindValue(":family", $unit->family_id, SQLITE3_INTEGER);
+                $statement->bindValue(":name", $unit->name, SQLITE3_TEXT);
+                $statement->bindValue(":element", get_element_id($unit->element), SQLITE3_INTEGER);
+                $statement->bindValue(":archetype", get_archetype_id($unit->archetype), SQLITE3_INTEGER);
+                $statement->bindValue(":base_stars", $unit->base_stars, SQLITE3_INTEGER);
+                $statement->bindValue(":natural_stars", $unit->natural_stars, SQLITE3_INTEGER);
+                $statement->bindValue(":obtainable", $unit->obtainable, SQLITE3_INTEGER);
+                $statement->bindValue(":can_awaken", $unit->can_awaken, SQLITE3_INTEGER);
+                $statement->bindValue(":awaken_bonus", $unit->awaken_bonus, SQLITE3_TEXT);
+                $statement->bindValue(":skill_ups_to_max", $unit->skill_ups_to_max, SQLITE3_INTEGER);
+                if (isset($unit->leader_skill->id)){
+                    $statement->bindValue(":leader_skill", $unit->leader_skill, SQLITE3_INTEGER);
+                }
+                else{
+                    $statement->bindValue(":leader_skill", null, SQLITE3_INTEGER);
+                }
+                $statement->bindValue(":hp", $unit->base_hp, SQLITE3_INTEGER);
+                $statement->bindValue(":attack", $unit->base_attack, SQLITE3_INTEGER);
+                $statement->bindValue(":defense", $unit->base_defense, SQLITE3_INTEGER);
+                $statement->bindValue(":speed", $unit->speed, SQLITE3_INTEGER);
+                $statement->bindValue(":crit_rate", $unit->crit_rate, SQLITE3_INTEGER);
+                $statement->bindValue(":crit_damage", $unit->crit_damage, SQLITE3_INTEGER);
+                $statement->bindValue(":resistance", $unit->resistance, SQLITE3_INTEGER);
+                $statement->bindValue(":accuracy", $unit->accuracy, SQLITE3_INTEGER);
+                $statement->bindValue(":raw_hp", $unit->raw_hp, SQLITE3_INTEGER);
+                $statement->bindValue(":raw_attack", $unit->raw_attack, SQLITE3_INTEGER);
+                $statement->bindValue(":raw_defense", $unit->raw_defense, SQLITE3_INTEGER);
+                $statement->bindValue(":max_lvl_hp", $unit->max_lvl_hp, SQLITE3_INTEGER);
+                $statement->bindValue(":max_lvl_attack", $unit->max_lvl_attack, SQLITE3_INTEGER);
+                $statement->bindValue(":max_lvl_defense", $unit->max_lvl_defense, SQLITE3_INTEGER);
+                $statement->bindValue(":awakens_from", get_com2us_id($unit->awakens_from), SQLITE3_TEXT);
+                $statement->bindValue(":awakens_to", get_com2us_id($unit->awakens_to), SQLITE3_TEXT);
+                $statement->bindValue(":fusion_food", $unit->fusion_food, SQLITE3_INTEGER);
+                $statement->bindValue(":homunculus", $unit->homunculus, SQLITE3_INTEGER);
+                $statement->bindValue(":craft_cost", 0, SQLITE3_INTEGER);
+                output(4, "QUERY: " . $statement->getSQL(true));
+                $statement->execute();
+
+                $url = 'https://swarfarm.com/static/herders/images/monsters/' . $unit->image_filename;   
+                $out = PATH . "public/img/unit/" . sprintf('%08d', $unit->com2us_id) . ".png";
+                download($url, $out);
+
+                // Skills (not in database yet, but they will be once skills are parsed)
+                foreach ($unit->skills as $skill){
+                    $statement = $db->prepare("INSERT INTO k_unit_skill (unit, skill) VALUES (:unit, :skill);");
+                    $statement->bindValue(":unit", $unit->com2us_id, SQLITE3_INTEGER);
+                    $statement->bindValue(":skill", $skill, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+
+                // Essences to awaken
+                foreach ($unit->awaken_cost as $essence){
+                    $statement = $db->prepare("INSERT INTO k_unit_essence (unit, item, amount) VALUES (:unit, :item, :amount);");
+                    $statement->bindValue(":unit", $unit->com2us_id, SQLITE3_INTEGER);
+                    $statement->bindValue(":item", $essence->item->com2us_id, SQLITE3_INTEGER);
+                    $statement->bindValue(":amount", $essence->quantity, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+
+                // Unit sources
+                foreach ($unit->source as $source){
+                    $statement = $db->prepare("INSERT INTO k_unit_source (unit, source) VALUES (:unit, :source);");
+                    $statement->bindValue(":unit", $unit->com2us_id, SQLITE3_INTEGER);
+                    $statement->bindValue(":source", $source->id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+            }
+            else{
+                // Compare all values
+                if ($r["family"] != $unit->family_id){
+                    $statement = $db->prepare("UPDATE k_unit SET family = :family WHERE id = :id;");
+                    $statement->bindValue(":family", $unit->family_id, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["name"] != $unit->name){
+                    $statement = $db->prepare("UPDATE k_unit SET name = :name WHERE id = :id;");
+                    $statement->bindValue(":name", $unit->name, SQLITE3_TEXT);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                $element = get_element_id($unit->element);
+                if ($r["element"] != $element){
+                    $statement = $db->prepare("UPDATE k_unit SET element = :element WHERE id = :id;");
+                    $statement->bindValue(":element", $element, SQLITE3_TEXT);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                $archetype = get_archetype_id($unit->archetype);
+                if ($r["archetype"] != $archetype){
+                    $statement = $db->prepare("UPDATE k_unit SET archetype = :archetype WHERE id = :id;");
+                    $statement->bindValue(":archetype", $archetype, SQLITE3_TEXT);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["base_stars"] != $unit->base_stars){
+                    $statement = $db->prepare("UPDATE k_unit SET base_stars = :base_stars WHERE id = :id;");
+                    $statement->bindValue(":base_stars", $unit->base_stars, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["natural_stars"] != $unit->natural_stars){
+                    $statement = $db->prepare("UPDATE k_unit SET natural_stars = :natural_stars WHERE id = :id;");
+                    $statement->bindValue(":natural_stars", $unit->natural_stars, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["obtainable"] != $unit->obtainable){
+                    $statement = $db->prepare("UPDATE k_unit SET obtainable = :obtainable WHERE id = :id;");
+                    $statement->bindValue(":obtainable", $unit->obtainable, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["can_awaken"] != $unit->can_awaken){
+                    $statement = $db->prepare("UPDATE k_unit SET can_awaken = :can_awaken WHERE id = :id;");
+                    $statement->bindValue(":can_awaken", $unit->can_awaken, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["awaken_bonus"] != $unit->awaken_bonus && $unit->awaken_bonus != 0){
+                    $statement = $db->prepare("UPDATE k_unit SET awaken_bonus = :awaken_bonus WHERE id = :id;");
+                    $statement->bindValue(":awaken_bonus", $unit->awaken_bonus, SQLITE3_TEXT);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_TEXT);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["skill_ups_to_max"] != $unit->skill_ups_to_max){
+                    $statement = $db->prepare("UPDATE k_unit SET skill_ups_to_max = :skill_ups_to_max WHERE id = :id;");
+                    $statement->bindValue(":skill_ups_to_max", $unit->skill_ups_to_max, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if (isset($unit->leader_skill->id) && $r["leader_skill"] != $unit->leader_skill->id){
+                    $statement = $db->prepare("UPDATE k_unit SET leader_skill = :leader_skill WHERE id = :id;");
+                    $statement->bindValue(":leader_skill", $unit->leader_skill->id, SQLITE3_TEXT);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["hp"] != $unit->base_hp){
+                    $statement = $db->prepare("UPDATE k_unit SET hp = :hp WHERE id = :id;");
+                    $statement->bindValue(":hp", $unit->base_hp, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["attack"] != $unit->base_attack){
+                    $statement = $db->prepare("UPDATE k_unit SET attack = :attack WHERE id = :id;");
+                    $statement->bindValue(":attack", $unit->base_attack, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["defense"] != $unit->base_defense){
+                    $statement = $db->prepare("UPDATE k_unit SET defense = :defense WHERE id = :id;");
+                    $statement->bindValue(":defense", $unit->base_defense, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["speed"] != $unit->speed){
+                    $statement = $db->prepare("UPDATE k_unit SET speed = :speed WHERE id = :id;");
+                    $statement->bindValue(":speed", $unit->speed, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["crit_rate"] != $unit->crit_rate){
+                    $statement = $db->prepare("UPDATE k_unit SET crit_rate = :crit_rate WHERE id = :id;");
+                    $statement->bindValue(":crit_rate", $unit->crit_rate, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["crit_damage"] != $unit->crit_damage){
+                    $statement = $db->prepare("UPDATE k_unit SET crit_damage = :crit_damage WHERE id = :id;");
+                    $statement->bindValue(":crit_damage", $unit->crit_damage, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["resistance"] != $unit->resistance){
+                    $statement = $db->prepare("UPDATE k_unit SET resistance = :resistance WHERE id = :id;");
+                    $statement->bindValue(":resistance", $unit->resistance, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["accuracy"] != $unit->accuracy){
+                    $statement = $db->prepare("UPDATE k_unit SET accuracy = :accuracy WHERE id = :id;");
+                    $statement->bindValue(":accuracy", $unit->accuracy, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["raw_hp"] != $unit->raw_hp){
+                    $statement = $db->prepare("UPDATE k_unit SET raw_hp = :raw_hp WHERE id = :id;");
+                    $statement->bindValue(":raw_hp", $unit->raw_hp, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["raw_attack"] != $unit->raw_attack){
+                    $statement = $db->prepare("UPDATE k_unit SET raw_attack = :raw_attack WHERE id = :id;");
+                    $statement->bindValue(":raw_attack", $unit->raw_attack, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["raw_defense"] != $unit->raw_defense){
+                    $statement = $db->prepare("UPDATE k_unit SET raw_defense = :raw_defense WHERE id = :id;");
+                    $statement->bindValue(":raw_defense", $unit->raw_defense, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["max_lvl_hp"] != $unit->max_lvl_hp){
+                    $statement = $db->prepare("UPDATE k_unit SET max_lvl_hp = :max_lvl_hp WHERE id = :id;");
+                    $statement->bindValue(":max_lvl_hp", $unit->max_lvl_hp, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["max_lvl_attack"] != $unit->max_lvl_attack){
+                    $statement = $db->prepare("UPDATE k_unit SET max_lvl_attack = :max_lvl_attack WHERE id = :id;");
+                    $statement->bindValue(":max_lvl_attack", $unit->max_lvl_attack, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["max_lvl_defense"] != $unit->max_lvl_defense){
+                    $statement = $db->prepare("UPDATE k_unit SET max_lvl_defense = :max_lvl_defense WHERE id = :id;");
+                    $statement->bindValue(":max_lvl_defense", $unit->max_lvl_defense, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                $com2us_id = get_com2us_id($unit->awakens_from);
+                if ($r["awakens_from"] != $com2us_id){
+                    $statement = $db->prepare("UPDATE k_unit SET awakens_from = :awakens_from WHERE id = :id;");
+                    $statement->bindValue(":awakens_from", $com2us_id, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                $com2us_id = get_com2us_id($unit->awakens_to);
+                if ($r["awakens_to"] != $com2us_id){
+                    $statement = $db->prepare("UPDATE k_unit SET awakens_to = :awakens_to WHERE id = :id;");
+                    $statement->bindValue(":awakens_to", $com2us_id, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["fusion_food"] != $unit->fusion_food){
+                    $statement = $db->prepare("UPDATE k_unit SET fusion_food = :fusion_food WHERE id = :id;");
+                    $statement->bindValue(":fusion_food", $unit->fusion_food, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+                if ($r["homunculus"] != $unit->homunculus){
+                    $statement = $db->prepare("UPDATE k_unit SET homunculus = :homunculus WHERE id = :id;");
+                    $statement->bindValue(":homunculus", $unit->homunculus, SQLITE3_INTEGER);
+                    $statement->bindValue(":id", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                }
+
+                // Loop skills. Only for inserting in k_skill and k_unit_skill
+                $statement = $db->prepare("SELECT count(skill) AS c FROM k_unit_skill WHERE unit = :unit;");
+                $statement->bindValue(":unit", $unit->com2us_id, SQLITE3_INTEGER);
+                $q = $statement->execute();
+                $r = $q->fetchArray(SQLITE3_ASSOC);
+                if ($r["c"] != sizeof($unit->skills)){
+                    $statement = $db->prepare("DELETE FROM k_unit_skill WHERE unit = :unit;");
+                    $statement->bindValue(":unit", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                    foreach ($unit->skills as $skill){
+                        $statement = $db->prepare("INSERT INTO k_unit_skill (unit, skill) VALUES (:unit, :skill);");
+                        $statement->bindValue(":unit", $unit->com2us_id, SQLITE3_INTEGER);
+                        $statement->bindValue(":skill", $skill, SQLITE3_INTEGER);
+                        output(4, "QUERY: " . $statement->getSQL(true));
+                        $statement->execute();
+                    }
+                }
+
+                // Essences to awaken
+                $statement = $db->prepare("SELECT count(item) AS c FROM k_unit_essence WHERE unit = :unit;");
+                $statement->bindValue(":unit", $unit->com2us_id, SQLITE3_INTEGER);
+                $q = $statement->execute();
+                $r = $q->fetchArray(SQLITE3_ASSOC);
+                if ($r["c"] != sizeof($unit->awaken_cost)){
+                    $statement = $db->prepare("DELETE FROM k_unit_essence WHERE unit = :unit;");
+                    $statement->bindValue(":unit", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                    foreach ($unit->awaken_cost as $essence){
+                        $statement = $db->prepare("INSERT INTO k_unit_essence (unit, item, amount) VALUES (:unit, :item, :amount);");
+                        $statement->bindValue(":unit", $unit->com2us_id, SQLITE3_INTEGER);
+                        $statement->bindValue(":item", $essence->item->com2us_id, SQLITE3_INTEGER);
+                        $statement->bindValue(":amount", $essence->quantity, SQLITE3_INTEGER);
+                        output(4, "QUERY: " . $statement->getSQL(true));
+                        $statement->execute();
+                    }
+                }
+
+                // Unit sources
+                $statement = $db->prepare("SELECT count(source) AS c FROM k_unit_source WHERE unit = :unit;");
+                $statement->bindValue(":unit", $unit->com2us_id, SQLITE3_INTEGER);
+                $q = $statement->execute();
+                $r = $q->fetchArray(SQLITE3_ASSOC);
+                if ($r["c"] != sizeof($unit->source)){
+                    $statement = $db->prepare("DELETE FROM k_unit_source WHERE unit = :unit;");
+                    $statement->bindValue(":unit", $unit->com2us_id, SQLITE3_INTEGER);
+                    output(4, "QUERY: " . $statement->getSQL(true));
+                    $statement->execute();
+                    foreach ($unit->source as $source){
+                        $statement = $db->prepare("INSERT INTO k_unit_source (unit, source) VALUES (:unit, :source);");
+                        $statement->bindValue(":unit", $unit->com2us_id, SQLITE3_INTEGER);
+                        $statement->bindValue(":source", $source->id, SQLITE3_INTEGER);
+                        output(4, "QUERY: " . $statement->getSQL(true));
+                        $statement->execute();
+                    }
+                }
+
+                // Image download
+                $out = PATH . "public/img/unit/" . sprintf('%08d', $unit->com2us_id) . ".png";
+                $url = 'https://swarfarm.com/static/herders/images/monsters/' . $unit->image_filename;   
+                download($url, $out);
+            }
+        }
+        //echo "NEXTPAGE: " . $next;
+    }
+
+    
+
+
+?>

+ 1 - 27
public/css/ui.css

@@ -1091,36 +1091,10 @@ table.artifact td.effects table tr.main_stat{
 }
 
 table.artifact td.details{
-    padding-left: 0.5em;
-    width: 16em;
     font-size: 80%;
-    border-collapse: collapse;
-}
-
-table.artifact td.details table.table_details td.title{text-align: right;}
-table.artifact td.details table.table_details td.value{
-    text-align: left;
-    padding-left: 1em;
-}
-
-table.artifact td.details table.table_details span.quality{
-    border: 0.1em solid #000000;
-    border-radius: 0.2em;
-    font-weight: bold;
-    text-shadow: 0 0 0.1em #000000;
-    padding: 0 0.3em;
-    width: 5em;
+    text-align: center;
 }
 
-table.artifact td.details table.table_details span.quality.quality_normal{background-color: #cccccc;}
-
-table.artifact td.details table.table_details span.quality.quality_magic{background-color: #55ff55;}
-
-table.artifact td.details table.table_details span.quality.quality_rare{background-color: #15c6e1;}
-
-table.artifact td.details table.table_details span.quality.quality_hero{background-color: #ad7fa8;}
-
-table.artifact td.details table.table_details span.quality.quality_legend{background-color: #f57900;}
 /**
  * /Artifact table
  */

BIN
public/img/skill/00014901.png


BIN
public/img/skill/00014902.png


BIN
public/img/skill/00014903.png


BIN
public/img/skill/00014904.png


BIN
public/img/skill/00014905.png


BIN
public/img/skill/00014906.png


BIN
public/img/skill/00014907.png


BIN
public/img/skill/00014908.png


BIN
public/img/skill/00014909.png


BIN
public/img/skill/00014910.png


BIN
public/img/skill/00014911.png


BIN
public/img/skill/00014912.png


BIN
public/img/skill/00014913.png


BIN
public/img/skill/00014914.png


BIN
public/img/skill/00014915.png


BIN
public/img/skill/00015001.png


BIN
public/img/skill/00015002.png


BIN
public/img/skill/00015003.png


BIN
public/img/skill/00015004.png


BIN
public/img/skill/00015005.png


BIN
public/img/skill/00015006.png


BIN
public/img/skill/00015007.png


BIN
public/img/skill/00015008.png


BIN
public/img/skill/00015009.png


BIN
public/img/skill/00015010.png


BIN
public/img/skill/00015011.png


BIN
public/img/skill/00015012.png


BIN
public/img/skill/00015013.png


BIN
public/img/skill/00015014.png


BIN
public/img/skill/00015015.png


BIN
public/img/unit/00025001.png


BIN
public/img/unit/00025002.png


BIN
public/img/unit/00025003.png


BIN
public/img/unit/00025004.png


BIN
public/img/unit/00025005.png


BIN
public/img/unit/00025011.png


BIN
public/img/unit/00025012.png


BIN
public/img/unit/00025013.png


BIN
public/img/unit/00025014.png


BIN
public/img/unit/00025015.png


BIN
public/img/unit/00025101.png


BIN
public/img/unit/00025102.png


BIN
public/img/unit/00025103.png


BIN
public/img/unit/00025104.png


BIN
public/img/unit/00025105.png


BIN
public/img/unit/00025111.png


BIN
public/img/unit/00025112.png


BIN
public/img/unit/00025113.png


BIN
public/img/unit/00025114.png


BIN
public/img/unit/00025115.png