Procházet zdrojové kódy

Merge branch 'Optimizer' into master

Iñigo Valentin před 5 roky
rodič
revize
87b30bc306

+ 1265 - 0
application/Constant.php

@@ -1498,6 +1498,1271 @@
         const ACC = 12;
     };
 
+
+    /**
+     * Rune main stat values constants.
+     *
+     * Includes the value of the main stats for type, stars, and level.
+     *
+     * @category Data
+     */
+    final class RUNE_STAT_VALUES{
+
+        /**
+         * 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.
      *

+ 21 - 1
application/Controller.php

@@ -120,6 +120,17 @@
                         action();
                         return;
                         break;
+                    case "OPTIMIZE":
+                        require_once(PATH::ACTION . "optimize.php");
+                        $response = action();
+                        if (strlen($response) > 1){
+                            echo($response);
+                            return;
+                        }
+                        else{
+                            return;
+                        }
+                        break;
                     case "EDIT_PROFILE":
                         require_once(PATH::ACTION . "edit_profile.php");
                         $response = action();
@@ -265,7 +276,16 @@
                         require_once(PATH::PAGE . "Report_Runecraft_Page.php");
                         $page = new Report_Runecraft_Page($pars[1]);
                     }
-
+                }
+                elseif (strtoupper($pars[0]) == "OPTIMIZER"){
+                    if (count($pars) == 1){
+                        require_once(PATH::PAGE . "Optimizer_Page.php");
+                        $page = new Optimizer_Page();
+                    }
+                    else{
+                        require_once(PATH::PAGE . "Optimizer_Unit_Page.php");
+                        $page = new Optimizer_Unit_Page($pars[1]);
+                    }
                 }
             }
             // Load the view, or set an error code.

+ 3 - 1
application/action/new_team.php

@@ -43,6 +43,7 @@
         $leader_id = filter_input(INPUT_POST, 'leader');
         $total_front = filter_input(INPUT_POST, 'front');
         $description = filter_input(INPUT_POST, 'description');
+        $score = 0;
         $id = preg_split("/[\s,]+/", $ids);
         $id_count = sizeof($id);
         if ($name == null || strlen($name) == 0){
@@ -170,7 +171,7 @@
         $q = $db->query($s);
         $r = $q->fetchArray(SQLITE3_ASSOC);
         $team_id = $r["id"];
-        $statement = $db->prepare('INSERT INTO team (uid, id, name, description, area_type, area, stage, difficulty) VALUES (:uid, :id, :name, :description, :area_type, :area, :stage, :difficulty);');
+        $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);
@@ -179,6 +180,7 @@
         $statement->bindValue(':area', $area);
         $statement->bindValue(':stage', $stage);
         $statement->bindValue(':difficulty', $difficulty);
+        $statement->bindValue(':score', $score);
         $res = $statement->execute();
         if (!$res){
             return -5;

+ 649 - 0
application/action/optimize.php

@@ -0,0 +1,649 @@
+<?php
+
+    /**
+     * File for the optimization action.
+     *
+     * Implements an action function to be called from the {@see Controller}.
+     * 
+     * @category Action
+     */
+
+    /**
+     * Executes the optimization action.
+     *
+     * Reads the POST parameters looking for the following KEYS:
+     *  mail
+     *  pass + currentPass
+     *  api
+     * Then it updates the selected info with the prameter vlue. Multiple
+     * itemas can be updated at the same time.
+     * 
+     * @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;
+        
+        $HARD_LIMIT = 100;
+
+        // Increase max execution time.
+        set_time_limit(60);
+        
+        $candidates = [
+            1 => [],
+            2 => [],
+            3 => [],
+            4 => [],
+            5 => [],
+            6 => []
+        ];
+        
+        $options = [];
+
+        $response = null;
+
+        $uid = filter_input(INPUT_POST, 'uid');
+        if ($uid == null){
+            return -1;
+        }
+
+        $unit_id = filter_input(INPUT_POST, 'unit');
+        if ($unit_id == null){
+            return -2;
+        }
+
+        // Sets
+        $sets = [];
+        foreach ($_POST["set"] as $x){
+            array_push($sets, intval($x));
+        }
+        if (sizeof($sets) < 2 || sizeof($sets) > 3){
+            return -3;
+        }
+
+        // Main stats in even slots.
+        $stats = [];
+        foreach ($_POST["stat"] as $x){
+            array_push($stats, intval($x));
+        }
+
+        $source = filter_input(INPUT_POST, 'source');
+        if ($source == null){
+            return -4;
+        }
+        
+        $limit = intval(filter_input(INPUT_POST, 'limit'));
+        if ($limit == 0){
+            return -5;
+        }
+
+        $tuning = filter_input(INPUT_POST, 'tuning');
+        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'))
+        ];
+
+        // Get Buld query:
+        $base_s = "
+          SELECT
+            id,
+            slot
+          FROM rune
+          WHERE
+            uid = $uid AND
+            type IN (
+        ";
+        foreach ($sets as $set){
+            $base_s .= ($set . ",");
+        }
+        $base_s .= "-1) AND ";
+        switch ($source){
+            case 0: // Storage only (or itself)
+                $base_s .= "
+                  (
+                    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 IS NULL OR 
+                    assigned_to NOT IN (SELECT DISTINCT unit FROM team_unit)
+                  )
+                ";
+                break;
+            case 2: // Units in teams with 0 score (or itself)
+                $base_s .= "
+                  (
+                    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 (
+                      SELECT DISTINCT unit 
+                      FROM
+                        team,
+                        team_unit
+                      WHERE
+                        team.id = team_unit.team AND
+                        team.score > 0
+                    )
+                  )
+                ";
+                break;
+            case 3: // Units with lower overall score (or itself)
+                // TODO
+                break;
+            case 4: // All runes - dont filter
+                $base_s .= " 1 = 1 ";
+                break;
+            default: // Invalid options - return nothing
+                $base_s .= " 1 = 0 ";
+        }
+        $s_order = " ORDER BY stars DESC, original_quality DESC, max_efficiency DESC, efficiency DESC, level DESC";
+
+        $s = ["", "", "", "", "", "", ""];
+        $s[1] = $base_s . " AND slot = 1 " . $s_order . " LIMIT 25";
+        $s[3] = $base_s . " AND slot = 3 " . $s_order . " LIMIT 25";
+        $s[5] = $base_s . " AND slot = 5 " . $s_order . " LIMIT 25";
+        $s[2] = $base_s . " AND slot = 2 AND main_stat = " . $stats[0] . $s_order . " LIMIT 20";
+        $s[4] = $base_s . " AND slot = 4 AND main_stat = " . $stats[1] . $s_order . " LIMIT 20";
+        $s[6] = $base_s . " AND slot = 6 AND main_stat = " . $stats[2] . $s_order . " LIMIT 20";
+
+        $total_candidates = 0;
+        for ($i = 1; $i <= 6; $i ++){
+            $q = $db->query($s[$i]);
+            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                array_push($candidates[$i], new Rune($r["id"]));
+                $total_candidates ++;
+            }
+        }
+        $max_combinations = sizeof($candidates[1]) * sizeof($candidates[2]) * sizeof($candidates[3]) * sizeof($candidates[4]) * sizeof($candidates[5]) * sizeof($candidates[6]);
+        if ($max_combinations == 0){
+            $res = [
+                "total" => 0,
+                "options" => []
+            ];
+            $response = json_encode($res);
+            return $response;
+        }
+        elseif ($max_combinations > 125000000){
+            // This should never happen unles query limits are changed
+            http_response_code(413); // Payload too large;
+            die();
+            return;
+        }
+        
+        // Get the unit and its base stats
+        $unit = new Unit($unit_id, true, $uid);
+        $unit_attack = $unit->attack + $unit->artifact_attack + $unit->building_attack;
+        $unit_base_attack = $unit->attack;
+        $unit_defense = $unit->defense + $unit->artifact_defense + $unit->building_defense;
+        $unit_base_defense = $unit->defense;
+        $unit_hp = $unit->hp + $unit->artifact_hp + $unit->building_hp;
+        $unit_base_hp = $unit->hp;
+        $unit_speed = $unit->speed + $unit->artifact_speed + $unit->building_speed;
+        $unit_base_speed = $unit->speed;
+        $unit_crit_rate = $unit->crit_rate + $unit->artifact_crit_rate + $unit->building_crit_rate;
+        $unit_base_crit_rate = $unit->crit_rate;
+        $unit_crit_damage = $unit->crit_damage + $unit->artifact_crit_damage + $unit->building_crit_damage;
+        $unit_base_crit_damage = $unit->crit_damage;
+        $unit_accuracy = $unit->accuracy + $unit->artifact_accuracy + $unit->building_accuracy;
+        $unit_base_accuracy = $unit->accuracy;
+        $unit_resistance = $unit->resistance + $unit->artifact_resistance + $unit->building_resistance;
+        $unit_base_resistance = $unit->resistance;
+
+        $i = [0, 0, 0, 0, 0, 0, 0]; // 7, so I can start with 1
+        while (true){
+            
+            
+            // Check if set combination is valid
+            if (valid_sets($candidates, $i, $sets) == true){
+                // Do things
+                $iteration = [
+                    "attack" => $unit_attack,
+                    "defense" => $unit_defense,
+                    "hp" => $unit_hp,
+                    "speed" => $unit_speed,
+                    "crit_rate" => $unit_crit_rate,
+                    "crit_damage" => $unit_crit_damage,
+                    "accuracy" => $unit_accuracy,
+                    "resistance" => $unit_resistance,
+                    "ehp" => 0,
+                    "dmg" => 0,
+                ];
+                for ($r = 1; $r <= 6; $r ++){
+                    // Loop selected runes and calculate stats
+                    $main_value = $candidates[$r][$i[$r]]->main_stat_value;
+                    if ($tuning == 1){ // All +12
+                        if ($candidates[$r][$i[$r]]->level < 12){
+                            $main_value = $candidates[$r][$i[$r]]->get_main_stat_at_level(12);
+                        }
+                    }
+                    elseif ($tuning == 2){ // Even +15, Odd + 12
+                        if ($candidates[$r][$i[$r]]->level < 15 && $candidates[$r][$i[$r]]->slot % 2 == 0){
+                            $main_value = $candidates[$r][$i[$r]]->get_main_stat_at_level(15);
+                        }
+                        elseif ($candidates[$r][$i[$r]]->level < 12 && $candidates[$r][$i[$r]]->slot % 2 != 0){
+                            $main_value = $candidates[$r][$i[$r]]->get_main_stat_at_level(12);
+                        }
+                    }
+                    if ($tuning == 3){ // All +15
+                        if ($candidates[$r][$i[$r]]->level < 15){
+                            $main_value = $candidates[$r][$i[$r]]->get_main_stat_at_level(15);
+                        }
+                    }
+                    sum_rune_stat(
+                      $candidates[$r][$i[$r]]->main_stat,
+                      $main_value,
+                      $iteration,
+                      $unit_base_attack,
+                      $unit_base_defense,
+                      $unit_base_hp,
+                    );
+                    sum_rune_stat(
+                      $candidates[$r][$i[$r]]->innate_stat,
+                      $candidates[$r][$i[$r]]->innate_stat_value,
+                      $iteration,
+                      $unit_base_attack,
+                      $unit_base_defense,
+                      $unit_base_hp,
+                    );
+                    sum_rune_stat(
+                      $candidates[$r][$i[$r]]->substat_1,
+                      $candidates[$r][$i[$r]]->substat_1_value,
+                      $iteration,
+                      $unit_base_attack,
+                      $unit_base_defense,
+                      $unit_base_hp,
+                    );
+                    sum_rune_stat(
+                      $candidates[$r][$i[$r]]->substat_2,
+                      $candidates[$r][$i[$r]]->substat_2_value,
+                      $iteration,
+                      $unit_base_attack,
+                      $unit_base_defense,
+                      $unit_base_hp,
+                    );
+                    sum_rune_stat(
+                      $candidates[$r][$i[$r]]->substat_3,
+                      $candidates[$r][$i[$r]]->substat_3_value,
+                      $iteration,
+                      $unit_base_attack,
+                      $unit_base_defense,
+                      $unit_base_hp,
+                    );
+                    sum_rune_stat(
+                      $candidates[$r][$i[$r]]->substat_4,
+                      $candidates[$r][$i[$r]]->substat_4_value,
+                      $iteration,
+                      $unit_base_attack,
+                      $unit_base_defense,
+                      $unit_base_hp,
+                    );
+                }
+                
+                // Calculate effective_hp
+                $hp = $iteration["hp"];
+                $def = $iteration["defense"];
+                $ehp = ceil(((($def * 3.5) + 1140) * $hp) / 1000);
+                $iteration["ehp"] = $ehp;
+
+                // Calculate effective_dmg
+                $atk = $iteration["attack"];
+                $crr = $iteration["crit_rate"];
+                if ($crr > 100){
+                    $crr = 100;
+                }
+                $crd = $iteration["crit_damage"];
+                $edmg = ceil(($atk * (100 - $crr) / 100) + (($atk + ($atk * $crd / 100)) * $crr / 100));
+                $iteration["dmg"] = $edmg;
+
+                // Compare with filters:
+                if (
+                  $iteration["attack"] >= $min["attack"] &&
+                  $iteration["defense"] >= $min["defense"] &&
+                  $iteration["hp"] >= $min["hp"] &&
+                  $iteration["speed"] >= $min["speed"] &&
+                  $iteration["crit_rate"] >= $min["crit_rate"] &&
+                  $iteration["crit_damage"] >= $min["crit_damage"] &&
+                  $iteration["accuracy"] >= $min["accuracy"] &&
+                  $iteration["resistance"] >= $min["resistance"] &&
+                  $iteration["ehp"] >= $min["ehp"] &&
+                  $iteration["dmg"] >= $min["dmg"]
+                ){
+
+                    // Calculate the variation
+                    $variation = 0;
+                    $variation += (($iteration["attack"] - $unit->total_attack) * 1);
+                    $variation += (($iteration["defense"] - $unit->total_defense) * 1);
+                    $variation += (($iteration["hp"] - $unit->total_hp) * (1 / 15));
+                    $variation += (($iteration["speed"] - $unit->total_speed) * 1.5);
+                    $variation += (($iteration["crit_rate"] - $unit->total_crit_rate) * 0.85);
+                    $variation += (($iteration["crit_damage"] - $unit->total_crit_damage) * 0.9);
+                    $variation += (($iteration["accuracy"] - $unit->total_accuracy) * 0.85);
+                    $variation += (($iteration["resistance"] - $unit->total_resistance) * 0.85);
+                    $variation = ceil($variation);
+
+                    // Calculate efficiency
+                    $efficiency = 0;
+                    
+                    // Build the array
+                    $option = [
+                        "variation" => $variation,
+                        "runes" => [
+                            [
+                                "id" => $candidates[1][$i[1]]->id,
+                                "html" => (HTML::rune_table($candidates[1][$i[1]]))
+                            ],
+                            [
+                                "id" => $candidates[2][$i[2]]->id,
+                                "html" => (HTML::rune_table($candidates[2][$i[2]]))
+                            ],
+                            [
+                                "id" => $candidates[3][$i[3]]->id,
+                                "html" => (HTML::rune_table($candidates[3][$i[3]]))
+                            ],
+                            [
+                                "id" => $candidates[4][$i[4]]->id,
+                                "html" => (HTML::rune_table($candidates[4][$i[4]]))
+                            ],
+                            [
+                                "id" => $candidates[5][$i[5]]->id,
+                                "html" => (HTML::rune_table($candidates[5][$i[5]]))
+                            ],
+                            [
+                                "id" => $candidates[1][$i[1]]->id,
+                                "html" => HTML::rune_table($candidates[6][$i[6]])
+                            ]
+                        ],
+                        "stats" => [
+                            "attack" => ceil($iteration["attack"]),
+                            "defense" => ceil($iteration["defense"]),
+                            "hp" => ceil($iteration["hp"]),
+                            "speed" => ceil($iteration["speed"]),
+                            "crit_rate" => ceil($iteration["crit_rate"]),
+                            "crit_damage" => ceil($iteration["crit_damage"]),
+                            "accuracy" => ceil($iteration["accuracy"]),
+                            "attack" => ceil($iteration["attack"]),
+                            "resistance" => ceil($iteration["resistance"]),
+                            "ehp" => ceil($iteration["ehp"]),
+                            "dmg" => ceil($iteration["dmg"])
+                        ]
+                    ];
+                    array_push($options, $option);
+                    if (sizeof($options) > 5000){
+                        http_response_code(413); // Payload too large;
+                        die();
+                        return;
+                    }
+                }
+
+            }
+
+            // Increase counters
+            $i[6] ++;
+            if ($i[6] == sizeof($candidates[6])){
+                $i[6] = 0;
+                $i[5] ++;
+            }
+            if ($i[5] == sizeof($candidates[5])){
+                $i[5] = 0;
+                $i[4] ++;
+            }
+            if ($i[4] == sizeof($candidates[4])){
+                $i[4] = 0;
+                $i[3] ++;
+            }
+            if ($i[3] == sizeof($candidates[3])){
+                $i[3] = 0;
+                $i[2] ++;
+            }
+            if ($i[2] == sizeof($candidates[2])){
+                $i[2] = 0;
+                $i[1] ++;
+            }
+
+            // Calculate exit condition
+            if (
+                $i[1] >= sizeof($candidates[1]) - 1 &&
+                $i[2] >= sizeof($candidates[2]) - 1 &&
+                $i[3] >= sizeof($candidates[3]) - 1 &&
+                $i[4] >= sizeof($candidates[4]) - 1 &&
+                $i[5] >= sizeof($candidates[5]) - 1 &&
+                $i[6] >= sizeof($candidates[6]) - 1
+            ){
+                break;
+            }
+        }
+
+        // Sort options by variation
+        usort($options, "sortOptions");
+
+        // Limit the array to the top options
+        $original_options_size = sizeof($options);
+        while (sizeof($options) > $limit || sizeof($options) > $HARD_LIMIT){
+            array_pop($options);
+        }
+        
+        
+        $res = [
+            "total" => sizeof($options),
+            "skipped" => ($original_options_size - sizeof($options)),
+            "options" => $options
+        ];
+        
+        $response = json_encode($res);
+        if ($response == null){
+            return 0;
+        }
+        else{
+            return $response;
+        }
+    }
+    
+    /**
+     * Validates a rune combination.
+     *
+     * Checks that set numbers are OK.
+     *
+     * @param \Rune[][] $candidates Candidate runes.
+     * @param int[] $indexes Currently selected indexes.
+     */
+    function valid_sets($candidates, $indexes, $requested){
+        
+        $sets = [
+            RUNE_SET_ID::ENERGY => 0,
+            RUNE_SET_ID::GUARD => 0,
+            RUNE_SET_ID::SWIFT => 0,
+            RUNE_SET_ID::BLADE => 0,
+            RUNE_SET_ID::RAGE => 0,
+            RUNE_SET_ID::FOCUS => 0,
+            RUNE_SET_ID::ENDURE => 0,
+            RUNE_SET_ID::FATAL => 0,
+            RUNE_SET_ID::DESPAIR => 0,
+            RUNE_SET_ID::VAMPIRE => 0,
+            RUNE_SET_ID::VIOLENT => 0,
+            RUNE_SET_ID::NEMESIS => 0,
+            RUNE_SET_ID::WILL => 0,
+            RUNE_SET_ID::SHIELD => 0,
+            RUNE_SET_ID::REVENGE => 0,
+            RUNE_SET_ID::DESTROY => 0,
+            RUNE_SET_ID::FIGHT => 0,
+            RUNE_SET_ID::DETERMINATION => 0,
+            RUNE_SET_ID::ENHANCE => 0,
+            RUNE_SET_ID::ACCURACY => 0,
+            RUNE_SET_ID::TOLERANCE => 0
+        ];
+        for ($i = 1; $i <= 6; $i++){
+            $sets[$candidates[$i][$indexes[$i]]->type->id] ++;
+        }
+        for ($i = 0; $i < sizeof($sets); $i ++){
+            switch ($i){
+                case RUNE_SET_ID::ENERGY:
+                case RUNE_SET_ID::GUARD:
+                case RUNE_SET_ID::NEMESIS:
+                case RUNE_SET_ID::BLADE:
+                case RUNE_SET_ID::FOCUS:
+                case RUNE_SET_ID::ENDURE:
+                case RUNE_SET_ID::WILL:
+                case RUNE_SET_ID::SHIELD:
+                case RUNE_SET_ID::REVENGE:
+                case RUNE_SET_ID::DESTROY:
+                case RUNE_SET_ID::FIGHT:
+                case RUNE_SET_ID::DETERMINATION:
+                case RUNE_SET_ID::ENHANCE:
+                case RUNE_SET_ID::ACCURACY:
+                case RUNE_SET_ID::TOLERANCE:
+                    if ($sets[$i] % 2 != 0){
+                        return false;
+                    }
+                    break;
+                
+                case RUNE_SET_ID::DESPAIR:
+                case RUNE_SET_ID::SWIFT:
+                case RUNE_SET_ID::RAGE:
+                case RUNE_SET_ID::FATAL:
+                case RUNE_SET_ID::VAMPIRE:
+                case RUNE_SET_ID::VIOLENT:
+                    if ($sets[$i] != 4 && $sets[$i != 0]){
+                        return false;
+                    }
+                    break;
+            }
+        }
+
+        // Sets are valid, now check if they comply with filters
+        $requested_sets = [
+            RUNE_SET_ID::ENERGY => 0,
+            RUNE_SET_ID::GUARD => 0,
+            RUNE_SET_ID::SWIFT => 0,
+            RUNE_SET_ID::BLADE => 0,
+            RUNE_SET_ID::RAGE => 0,
+            RUNE_SET_ID::FOCUS => 0,
+            RUNE_SET_ID::ENDURE => 0,
+            RUNE_SET_ID::FATAL => 0,
+            RUNE_SET_ID::DESPAIR => 0,
+            RUNE_SET_ID::VAMPIRE => 0,
+            RUNE_SET_ID::VIOLENT => 0,
+            RUNE_SET_ID::NEMESIS => 0,
+            RUNE_SET_ID::WILL => 0,
+            RUNE_SET_ID::SHIELD => 0,
+            RUNE_SET_ID::REVENGE => 0,
+            RUNE_SET_ID::DESTROY => 0,
+            RUNE_SET_ID::FIGHT => 0,
+            RUNE_SET_ID::DETERMINATION => 0,
+            RUNE_SET_ID::ENHANCE => 0,
+            RUNE_SET_ID::ACCURACY => 0,
+            RUNE_SET_ID::TOLERANCE => 0
+        ];
+        for ($i = 0; $i < sizeof($requested); $i ++){
+            switch ($requested[$i]){
+                case RUNE_SET_ID::ENERGY:
+                case RUNE_SET_ID::GUARD:
+                case RUNE_SET_ID::NEMESIS:
+                case RUNE_SET_ID::BLADE:
+                case RUNE_SET_ID::FOCUS:
+                case RUNE_SET_ID::ENDURE:
+                case RUNE_SET_ID::WILL:
+                case RUNE_SET_ID::SHIELD:
+                case RUNE_SET_ID::REVENGE:
+                case RUNE_SET_ID::DESTROY:
+                case RUNE_SET_ID::FIGHT:
+                case RUNE_SET_ID::DETERMINATION:
+                case RUNE_SET_ID::ENHANCE:
+                case RUNE_SET_ID::ACCURACY:
+                case RUNE_SET_ID::TOLERANCE:
+                    $requested_sets[$requested[$i]] += 2;
+                    break;
+                case RUNE_SET_ID::DESPAIR:
+                case RUNE_SET_ID::SWIFT:
+                case RUNE_SET_ID::RAGE:
+                case RUNE_SET_ID::FATAL:
+                case RUNE_SET_ID::VAMPIRE:
+                case RUNE_SET_ID::VIOLENT:
+                    $requested_sets[$requested[$i]] += 4;
+                    break;
+            }
+        }
+
+        if ($sets === $requested_sets){
+            return true;
+        }
+        else{
+            return false;
+        }
+    }
+
+    /**
+     * Calculates the stat increase by a rune property.
+     * 
+     * @param string $stat Stat type.
+     * @param int $value Stat increase value.
+     * @param int[] Reference to iteration stats array.
+     */
+    function sum_rune_stat($stat, $value, &$iteration, $base_atk, $base_def, $base_hp){
+        switch ($stat){
+            case RUNE_STAT_ID::ATK:
+                $iteration["attack"] += $value;
+                break;
+            case RUNE_STAT_ID::DEF:
+                $iteration["defense"] += $value;
+                break;
+            case RUNE_STAT_ID::HP:
+                $iteration["hp"] += $value;
+                break;
+            case RUNE_STAT_ID::SPD:
+                $iteration["speed"] += $value;
+                break;
+            case RUNE_STAT_ID::CRR:
+                $iteration["crit_rate"] += $value;
+                break;
+            case RUNE_STAT_ID::CRD:
+                $iteration["crit_damage"] += $value;
+                break;
+            case RUNE_STAT_ID::ACC:
+                $iteration["accuracy"] += $value;
+                break;
+            case RUNE_STAT_ID::RES:
+                $iteration["resistance"] += $value;
+                break;
+            case RUNE_STAT_ID::ATK_P:
+                $iteration["attack"] += ($base_atk * $value / 100);
+                break;
+            case RUNE_STAT_ID::DEF_P:
+                $iteration["defense"] += ($base_def * $value / 100);
+                break;
+            case RUNE_STAT_ID::HP_P:
+                $iteration["hp"] += ($base_hp * $value / 100);
+                break;
+        }
+    }
+    
+    /**
+     * Function to sort arrays based on the "increment" key.
+     *
+     * @param $a An array
+     * @param $b Other array
+     * @return int Positive if $b before $a, negative otherwise. 
+     */
+    function sortOptions($a, $b) {
+        return $b["variation"] - $a["variation"];
+    }
+?>

+ 2 - 1
application/action/save_run_team.php

@@ -40,7 +40,7 @@
         if (!$r){
             return -2;
         }
-        $statement = $db->prepare('INSERT INTO team (uid, id, name, description, area_type, area, stage, difficulty) VALUES (:uid, :id, :name, :description, :area_type, :area, :stage, :difficulty);');
+        $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);
@@ -49,6 +49,7 @@
         $statement->bindValue(':area', $r["area"]);
         $statement->bindValue(':stage', $r["stage"]);
         $statement->bindValue(':difficulty', $r["difficulty"]);
+        $statement->bindValue(':score', $score);
         $res = $statement->execute();
         if (!$res){
             return -3;

+ 2 - 1
application/entity/K_Unit.php

@@ -273,8 +273,8 @@
               "WHERE id = $id; ";
             $q = $db->query($s);
             $r = $q->fetchArray(SQLITE3_ASSOC);
+            $this->id = $id;
             if ($r){
-                $this->id = $r["id"];
                 $this->family = $r["family"];
                 $this->name = HTML::e($r["name"]);
                 $this->element = $r["element"];
@@ -398,6 +398,7 @@
               "WHERE " .
               "  unit = " . $this->id . " " .
               "ORDER BY item; ";
+            error_log($s);
             $q = $db->query($s);
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 $e = [

+ 39 - 0
application/entity/Rune.php

@@ -316,6 +316,45 @@
             }
         }
 
+        /**
+         * Gets the value of the main stat at an arbitrary level.
+         *
+         * @param int $level Desired level.
+         * @return int Value of the main stat at the selected level.
+         */
+        public function get_main_stat_at_level($level){
+            $level = intval($level);
+            if ($level < 0 || $level > 15){
+                return 0;
+            }
+            switch ($this->main_stat){
+                    case RUNE_STAT_ID::HP:
+                        return RUNE_STAT_VALUES::HP[$this->stars][$level];
+                    case RUNE_STAT_ID::HP_P:
+                        return RUNE_STAT_VALUES::HP_P[$this->stars][$level];
+                    case RUNE_STAT_ID::ATK:
+                        return RUNE_STAT_VALUES::ATK[$this->stars][$level];
+                    case RUNE_STAT_ID::ATK_P:
+                        return RUNE_STAT_VALUES::ATK_P[$this->stars][$level];
+                    case RUNE_STAT_ID::DEF:
+                        return RUNE_STAT_VALUES::DEF[$this->stars][$level];
+                    case RUNE_STAT_ID::DEF_P:
+                        return RUNE_STAT_VALUES::DEF_P[$this->stars][$level];
+                    case RUNE_STAT_ID::SPD:
+                        return RUNE_STAT_VALUES::SPD[$this->stars][$level];
+                    case RUNE_STAT_ID::CRR:
+                        return RUNE_STAT_VALUES::CRR[$this->stars][$level];
+                    case RUNE_STAT_ID::CRD:
+                        return RUNE_STAT_VALUES::CRD[$this->stars][$level];
+                    case RUNE_STAT_ID::RES:
+                        return RUNE_STAT_VALUES::RES[$this->stars][$level];
+                    case RUNE_STAT_ID::ACC:
+                        return RUNE_STAT_VALUES::ACC[$this->stars][$level];
+                    default:
+                        return 0;
+            }
+        }
+
         /**
          * Calculates name variables.
          */

+ 8 - 1
application/entity/Team.php

@@ -74,6 +74,11 @@
          */
         public $difficulty;
 
+        /**
+         * @var int Team score for the optmizer
+         */
+        public $score;
+
         /**
          * Constructor.
          *
@@ -97,7 +102,8 @@
                 area_type,
                 area,
                 stage,
-                difficulty
+                difficulty,
+                score
               FROM team
               WHERE id = '$id';
             ";
@@ -109,6 +115,7 @@
             $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

+ 5 - 2
application/entity/Unit.php

@@ -371,9 +371,12 @@
          * @global int Player ID.
          * @global resource Database connection.
          */
-        public function __construct($id, $complete = true){
+        public function __construct($id, $complete = true, $uid = 0){
             global $UID;
             global $db;
+            if ($uid == 0){
+                $uid = $UID;
+            }
             $s =
               "SELECT " .
               "  uid, " .
@@ -400,7 +403,7 @@
               "  lock " .
               "FROM unit " .
               "WHERE " .
-              "  uid = '$UID' AND " .
+              "  uid = '$uid' AND " .
               "  id = '$id'; ";
             $q = $db->query($s);
             $r = $q->fetchArray(SQLITE3_ASSOC);

+ 72 - 0
application/page/Optimizer_Page.php

@@ -0,0 +1,72 @@
+ <?php
+    /**
+     * Optimizer team list 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");
+    require_once(PATH::ENTITY . "Team.php");
+
+    /**
+     * Optimizer team list page model.
+     *
+     * @category Page
+     */
+    class Optimizer_Page extends Page{
+
+        /**
+         * @var \Team[] Teams to show.
+         */
+        public $teams = [];
+
+        /**
+         * Constructor.
+         *
+         * Retrieves the data and initializes the variables.
+         *
+         * @global int Player ID.
+         * @global resource Database connection.
+         */
+        public function __construct(){
+            global $UID;
+            global $db;
+            $this->view = PATH::VIEW . "optimizer.php";
+            $s = $this->build_query();
+            $q = $db->query($s);
+            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                array_push($this->teams, new Team($r["id"]));
+            }
+            $s = "
+              SELECT id
+              FROM unit
+              WHERE unit.uid = '$UID';
+            ";
+            $this->title = "Optmizer - SWDB";
+            $this->description = "Optimizer";
+            $this->canonical = URL::BASE . "optmizer/";
+        }
+
+        /**
+         * Builds the query to the rune table using the selected or default
+         * filters.
+         *
+         * @return The query to be executed.
+         */
+        private function build_query(){
+            global $UID;
+            $s = "
+              SELECT id
+              FROM team
+              WHERE uid = '$UID'
+            ";
+            return $s;
+        }
+
+    }
+?>

+ 43 - 0
application/page/Optimizer_Unit_Page.php

@@ -0,0 +1,43 @@
+ <?php
+    /**
+     * Optimizer unit 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");
+    require_once(PATH::ENTITY . "Unit.php");
+
+    /**
+     * Optimizer team list page model.
+     *
+     * @category Page
+     */
+    class Optimizer_Unit_Page extends Page{
+
+        /**
+         * @var Unit The unit to optimize.
+         */
+        public $unit;
+
+        /**
+         * Constructor.
+         *
+         * Retrieves the data and initializes the variables.
+         *
+         * @param string $id Selected unit id
+         */
+        public function __construct($id){
+            $this->view = PATH::VIEW . "optimizer_unit.php";
+            $this->unit = new Unit($id);
+            $this->title = "Optimize " . $this->unit->unit->name . " - SWDB";
+            $this->description = "Optimize " . $this->unit->unit->name;
+            $this->canonical = URL::BASE . "optmizer/$id";
+        }
+    }
+?>

binární
application/sw.sqlite


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

@@ -57,6 +57,9 @@
             <a href='/<?=$UID?>/report/'>
                 Reports
             </a>
+            <a href='/<?=$UID?>/optimizer/'>
+                Optimizer
+            </a>
         </nav>
         <nav id='profile'>
             <a href='/<?=$UID?>/profile/'>

+ 244 - 0
application/view/optimizer.php

@@ -0,0 +1,244 @@
+<?php
+    /**
+     * Optimizer teams view.
+     *
+     * 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.
+     */
+?>
+<!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?>optimizer.css'/>
+        <link rel='stylesheet' type='text/css' href='<?=URL::CSS?>filters.css'/>
+        <!-- Meta tags -->
+        <link rel='canonical' href='<?=$page->canonical?>'/>
+        <link rel='author' href='<?=$page->author?>'/>
+        <link rel='publisher' href='<?=$page->author?>'/>
+        <meta name='description' content='<?=$page->description?>'/>
+        <meta property='og:title' content='<?=$page->title?>'/>
+        <meta property='og:url' content='<?=$page->canonical?>'/>
+        <meta property='og:description' content='<?=$page->description?>'/>
+        <meta property='og:image' content='<?=$page->icon?>'/>
+        <meta property='og:site_name' content='<?=$page->name?>'/>
+        <meta property='og:type' content='website'/>
+        <meta property='og:locale' content='en'/>
+        <meta name='twitter:card' content='summary'/>
+        <meta name='twitter:title' content='<?=$page->title?>'/>
+        <meta name='twitter:description' content='<?=$page->description?>'/>
+        <meta name='twitter:image' content='<?=$page->icon?>'/>
+        <meta name='twitter:url' content='<?=$page->canonical?>'/>
+        <meta name='robots' content='index follow'/>
+        <script>
+            units = [];
+<?php
+            $units_added = [];
+            foreach ($page->teams as $team){
+                foreach ($team->unit as $unit){
+                    if (!in_array($unit->id, $units_added)){
+                        array_push($units_added, $unit->id);
+                        $panel = json_encode("<div class='monster_panel'>" . HTML::unit_panel($unit) . "</div>");
+?>
+                        units.push(
+                            {
+                                id: "<?=$unit->id?>",
+                                score: <?=$team->score?>,
+                                panel: <?=$panel?>
+                            }
+                        );
+<?php
+                    }
+                    else{
+                        // Find in array and increase score
+?>
+                        for (let i = 0; i < units.length; i++) {
+                            if (units[i].id == "<?=$unit->id?>"){
+                                units[i].score += <?=$team->score?>;
+                                break;
+                            }
+                        }
+<?php
+                    }
+                }
+            }
+?>
+            /**
+             * Changes the score of a team.
+             *
+             * @param team ID of the team to change.
+             * @param score New score.
+             * @return true on success, false on error.
+             */
+            function changeScore(team, score){
+                // TODO
+                return true;
+            }
+
+            /**
+             * Redirect to a unit optimization page.
+             *
+             * @param unit ID of the unit to optimize.
+             */
+            function optimize(unit){
+                window.location.href = "/<?=$UID?>/optimizer/" + unit;
+            }
+
+            /**
+             * Builds the unitpriority table.
+             *
+             * Calculates the score of each unit acording to teams score, and
+             * builds a sorted table enabling a link to optimization.
+             */
+            function calculateScores(){
+                // Sort array, descending score
+                units = units.sort(function(a, b){return b.score - a.score;});
+
+                // Draw table
+                const container = document.getElementById('unit_list');
+                while (container.firstChild) {
+                    container.removeChild(container.lastChild);
+                }
+                let table = document.createElement('table');
+                let tr = document.createElement('tr');
+                let th = document.createElement('th');
+                th.innerHTML = 'Unit';
+                th.setAttribute('class', 'unit');
+                tr.appendChild(th);
+                th = document.createElement('th');
+                th.setAttribute('class', 'score');
+                th.innerHTML = 'Score';
+                tr.appendChild(th);
+                th = document.createElement('th');
+                th.innerHTML = 'Action';
+                th.setAttribute('class', 'action');
+                tr.appendChild(th);
+                table.appendChild(tr);
+                var odd = 'odd';
+                for (let i = 0; i < units.length; i++) {
+                    let tr = document.createElement('tr');
+                    let td = document.createElement('td');
+                    td.setAttribute('class', 'unit ' + odd);
+                    td.innerHTML = units[i].panel;
+                    tr.appendChild(td);
+                    td = document.createElement('td');
+                    td.setAttribute('class', 'score ' + odd);
+                    td.innerHTML = units[i].score;
+                    tr.appendChild(td);
+                    td = document.createElement('td');
+                    td.setAttribute('class', 'action ' + odd);
+                    btn = document.createElement('input');
+                    btn.setAttribute('type', 'button');
+                    btn.setAttribute('value', 'Optimize');
+                    btn.setAttribute('onclick', 'optimize("' + units[i].id + '")'); 
+                    td.appendChild(btn);
+                    tr.appendChild(td);
+                    table.appendChild(tr);
+                    if (odd == ''){
+                        odd = 'odd';
+                    }
+                    else{
+                        odd = '';
+                    }
+                }
+                container.appendChild(table);
+            }
+
+        </script>
+    </head>
+    <body onLoad='calculateScores();'>
+<?php
+        include __DIR__ . "/inc/header.php";
+?>
+        <section class='list'>
+            <h2>
+                Teams
+            </h2>
+            <article>
+                <table id='teams'>
+                    <tr>
+                        <th>
+                            Team
+                        </th>
+                        <th>
+                            Description
+                        </th>
+                        <th>
+                            Units
+                        </th>
+                        <th>
+                            Score
+                        </th>
+                    </tr>
+<?php
+                    $odd = "odd";
+                    foreach ($page->teams as $team){
+?>
+                        <tr>
+                            <td class='team <?=$odd?>'>
+                                <?=$team->name?>
+                            </td>
+                            <td class='description <?=$odd?>'>
+                                <?=$team->description?>
+                            </td>
+                            <td class='units <?=$odd?>'>
+<?php
+                                $prev_front = false;
+                                $front = "undefined";
+                                foreach ($team->unit as $unit){
+                                    $front = in_array($unit->id, $team->frontline);
+                                    if ($front != "undefined" && $prev_front != $front){
+?>
+                                        <hr class='frontline'/>
+<?php
+                                    }
+?>
+                                    <a href='/<?=$UID?>/monsters/<?=$unit->id?>' target='_blank'>
+                                        <div class='monster_panel'>
+                                            <?=HTML::unit_panel($unit)?>
+<?php
+                                            if ($unit->id == $team->leader){
+?>
+                                                <img alt='Leader' class='leader' src='<?=URL::IMG["ICON"]?>leader.png'/>
+<?php
+                                            }
+?>
+                                        </div>
+                                    </a>
+<?php
+                                    $prev_front = $front;
+                                }
+?>
+                            </td>
+                            <td class='score <?=$odd?>'>
+                                <input type='number' min='0' max='20' value='<?=$team->score?>' onChange='changeScore(<?=$team->id?>, this.value);'/>
+                            </td>
+                        </tr>
+<?php
+                        if ($odd == ""){
+                            $odd = "odd";
+                        }
+                        else{
+                            $odd = "";
+                        }
+                    }
+?>
+                </table>
+                <div id='unit_list'>
+                </div>
+            </article>
+        </section> <!-- .list -->
+
+<?php
+        include __DIR__ . "/inc/footer.php";
+?>
+    </body>
+</html>

+ 1353 - 0
application/view/optimizer_unit.php

@@ -0,0 +1,1353 @@
+<?php
+    /**
+     * Monster view.
+     *
+     * 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.
+     */
+?>
+<!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?>optimizer.css'/>
+        <!-- Meta tags -->
+        <link rel='canonical' href='<?=$page->canonical?>'/>
+        <link rel='author' href='<?=$page->author?>'/>
+        <link rel='publisher' href='<?=$page->author?>'/>
+        <meta name='description' content='<?=$page->description?>'/>
+        <meta property='og:title' content='<?=$page->title?>'/>
+        <meta property='og:url' content='<?=$page->canonical?>'/>
+        <meta property='og:description' content='<?=$page->description?>'/>
+        <meta property='og:image' content='<?=$page->icon?>'/>
+        <meta property='og:site_name' content='<?=$page->name?>'/>
+        <meta property='og:type' content='website'/>
+        <meta property='og:locale' content='en'/>
+        <meta name='twitter:card' content='summary'/>
+        <meta name='twitter:title' content='<?=$page->title?>'/>
+        <meta name='twitter:description' content='<?=$page->description?>'/>
+        <meta name='twitter:image' content='<?=$page->icon?>'/>
+        <meta name='twitter:url' content='<?=$page->canonical?>'/>
+        <meta name='robots' content='index follow'/>
+        <script>
+
+            /**
+             * Unit current stats.
+             */
+            var current_stats = {
+                "attack": <?=$page->unit->total_attack?>,
+                "defense": <?=$page->unit->total_defense?>,
+                "hp": <?=$page->unit->total_hp?>,
+                "speed": <?=$page->unit->total_speed?>,
+                "crit_rate": <?=$page->unit->total_crit_rate?>,
+                "crit_damage": <?=$page->unit->total_crit_damage?>,
+                "accuracy": <?=$page->unit->total_accuracy?>,
+                "resistance": <?=$page->unit->total_resistance?>,
+                "ehp": <?=$page->unit->effective_hp?>,
+                "dmg": <?=$page->unit->effective_dmg?>
+            }
+
+<?php
+            // Get current rune sets
+            $sets = [];
+            foreach ($page->unit->rune as $rune){
+                if (!in_array($rune->type->id, $sets)){
+                    array_push($sets, $rune->type->id);
+                }
+            }
+            for ($i = sizeof($sets); $i < 3; $i ++){
+                array_push($sets, 0);
+            }
+?>
+            var current_sets = [<?=$sets[0]?>, <?=$sets[1]?>, <?=$sets[2]?>];
+
+            // Get current main stats in even slots
+<?php
+            $mains = [0, 0, 0];
+            foreach ($page->unit->rune as $rune){
+                if ($rune->slot % 2 == 0){
+                    $mains[($rune->slot / 2) - 1] = $rune->main_stat;
+                }
+            }
+?>
+
+            /**
+             * To calculate time.
+             */
+            var timestamp = 0;
+
+            /**
+             * Updates the value next to the sliders when they change.
+             *
+             * @param stat Name of the stat to set.
+             * @param value Value of the slider.
+             */
+            function updateMinValue(stat, element){
+                var value = Number(element.value);
+                var max = Number(element.max);
+                var min = Number(element.min);
+                if (value < min){
+                    value = min;
+                }
+                if (value > max){
+                    value = max;
+                }
+                document.getElementById('lbl_min_' + stat).value = value;
+                document.getElementById('min_' + stat).value = value;
+            }
+            
+            /**
+             * Resets filter to the unit's current stat status.
+             *
+             * @param stat Name of the stat to set.
+             */
+            function resetFilter(stat){
+                document.getElementById('lbl_min_' + stat).value = current_stats[stat];
+                document.getElementById('min_' + stat).value = current_stats[stat];
+            }
+
+            /**
+             * Sets a filter to it's lowest possible value.
+             *
+             * @param stat Name of the stat to set.
+             */
+            function removeFilter(stat){
+                document.getElementById('lbl_min_' + stat).value = document.getElementById('lbl_min_' + stat).min;
+                document.getElementById('min_' + stat).value = document.getElementById('min_' + stat).min;
+            }
+
+            /**
+             * Starts the optimization.
+             *
+             * Checks for error in the combination of the parameters, and
+             * shows an error window if there is an invalid conbination. If
+             * not, builds the url to calculate the new rune combinations,
+             * and presents th received response.
+             *
+             * @return false on error, true on success.
+             */
+            function apply(){
+
+                var params = '';
+                var x;
+
+                // Player id - mandatory
+                params = 'uid=<?=$UID?>';
+
+                // Unit id - mandatory
+                params += '&unit=<?=$page->unit->id?>';
+
+                // Rune set 1 - mandatory
+                params += '&set[]=' + document.getElementById('set_1').options[document.getElementById('set_1').selectedIndex].value;
+
+                // Rune set 2 - mandatory
+                params += '&set[]=' + document.getElementById('set_2').options[document.getElementById('set_2').selectedIndex].value;
+
+                // Rune set 3 - optional
+                x = document.getElementById('set_3').options[document.getElementById('set_3').selectedIndex].value;
+                if (x != ''){
+                    params += '&set[]=' + x;
+                }
+
+                // Slot 2 - mandatory
+                params += '&stat[]=' + document.getElementById('slot_2').options[document.getElementById('slot_2').selectedIndex].value;
+
+                // Slot 4 - mandatory
+                params += '&stat[]=' + document.getElementById('slot_4').options[document.getElementById('slot_4').selectedIndex].value;
+
+                // Slot 5 - mandatory
+                params += '&stat[]=' + document.getElementById('slot_6').options[document.getElementById('slot_6').selectedIndex].value;
+
+                // Rune source - mandatory
+                params += '&source=' + document.getElementById('rune_source').options[document.getElementById('rune_source').selectedIndex].value;
+
+                // Optimizer tuning - mandatory
+                params += '&tuning=' + document.getElementById('tuning').options[document.getElementById('tuning').selectedIndex].value;
+
+                // Limit combinations - mandatory
+                params += '&limit=' + document.getElementById('limit').value;
+
+                // Min HP - Optional
+                if (Number(document.getElementById('min_hp').value) > Number(document.getElementById('min_hp').min)){
+                    params += '&min_hp=' + document.getElementById('min_hp').value;
+                }
+
+                // Min ATK - Optional
+                if (Number(document.getElementById('min_attack').value) > Number(document.getElementById('min_attack').min)){
+                    params += '&min_attack=' + document.getElementById('min_attack').value;
+                }
+
+                // Min DEF - Optional
+                if (Number(document.getElementById('min_defense').value) > Number(document.getElementById('min_defense').min)){
+                    params += '&min_defense=' + document.getElementById('min_defense').value;
+                }
+
+                // Min SPD - Optional
+                if (Number(document.getElementById('min_speed').value) > Number(document.getElementById('min_speed').min)){
+                    params += '&min_speed=' + document.getElementById('min_speed').value;
+                }
+
+                // Min CRR - Optional
+                if (Number(document.getElementById('min_crit_rate').value) > Number(document.getElementById('min_crit_rate').min)){
+                    params += '&min_crit_rate=' + document.getElementById('min_crit_rate').value;
+                }
+
+                // Min CRD - Optional
+                if (Number(document.getElementById('min_crit_damage').value) > Number(document.getElementById('min_crit_damage').min)){
+                    params += '&min_crit_damage=' + document.getElementById('min_crit_damage').value;
+                }
+
+                // Min ACC - Optional
+                if (Number(document.getElementById('min_accuracy').value) > Number(document.getElementById('min_accuracy').min)){
+                    params += '&min_accuracy=' + document.getElementById('min_accuracy').value;
+                }
+
+                // Min RES - Optional
+                if (Number(document.getElementById('min_resistance').value) > Number(document.getElementById('min_resistance').min)){
+                    params += '&min_resistance=' + document.getElementById('min_resistance').value;
+                }
+
+                // Min EHP - Optional
+                if (Number(document.getElementById('min_ehp').value) > 0){
+                    params += '&min_ehp=' + document.getElementById('min_ehp').value;
+                }
+
+                // Min DMG - Optional
+                if (Number(document.getElementById('min_dmg').value) > 0){
+                    params += '&min_dmg=' + document.getElementById('min_dmg').value;
+                }
+
+                // Send th request
+                var xhttp = new XMLHttpRequest();
+                xhttp.onreadystatechange = function() {
+                    if (this.readyState == 4){
+                        document.getElementById('apply').removeAttribute('disabled');
+                        document.getElementById('wait').style.display = 'none';
+                        if (this.status == 200) {
+                            time = ((+ new Date()) - timestamp) / 1000;
+                            showOptimizations(this.response, time);
+                        }
+                        else if (this.status == 413) { // Payload too large
+                            showMessage('Error', 'Too many combination found. Plesase narow your criteria and retry.');
+                        }
+                        else if (this.status == 500) { // Server error
+                            showMessage('Error', 'Too many rune combination found. Plesase narow your criteria and retry.');
+                        }
+                    }
+                };
+
+                // DEBUG
+                //params = 'uid=8114048&unit=6441802832&limit=6&set[]=2&set[]=4&set[]=4&stat[]=4&stat[]=4&stat[]=4&source=0&tuning=2&min_defense=800';
+
+
+                // Clear result table, disable button and show spinner
+                var container = document.getElementById('optimizations');
+                while (container.firstChild) {
+                    container.removeChild(container.firstChild);
+                }
+                document.getElementById('apply').setAttribute('disabled', 'disabled');
+                document.getElementById('wait').style.display = 'block';
+                document.getElementById('results').style.display = 'none';
+                
+                // Start timer
+                timestamp = + new Date();
+
+                // Make the request
+                xhttp.open('POST', '/action/optimize/', true);
+                xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+                xhttp.send(params);
+            }
+
+            /**
+             * Parses the received content and creates the HTML with the new
+             * rune options.
+             *
+             * @param content JSON received from server.
+             */
+            function showOptimizations(content, time){
+                var container = document.getElementById('optimizations');
+                var data = JSON.parse(content);
+                if (Number(data.total) == 0){
+                    showMessage('No results', 'No suitable combination found. Plesase relax your criteria and retry.');
+                    return;
+                }
+                document.getElementById('num_results').innerHTML = data.total;
+                document.getElementById('num_skipped').innerHTML = data.skipped;
+                document.getElementById('num_seconds').innerHTML = time;
+                var option;
+                var tr;
+                var td;
+                var stat_table;
+                var stat_tr;
+                var stat_td;
+                var span;
+                var text;
+                for (i = 0; i < data.options.length; i++){
+                    option = data.options[i];
+                    tr = document.createElement('tr');
+                    td = document.createElement('td');
+                    td.setAttribute('class', 'option_stats');
+                    stat_table = document.createElement('table');
+                    stat_table.setAttribute('class', 'stat_table');
+
+                    // attack
+                    stat_tr = document.createElement('tr');
+                    stat_td = document.createElement('td');
+                    stat_td.setAttribute('class', 'label');
+                    text = document.createTextNode('ATK');
+                    stat_td.appendChild(text);
+                    stat_tr.appendChild(stat_td);
+                    stat_td = document.createElement('td');
+                    stat_td.setAttribute('class', 'value');
+                    span = document.createElement('span');
+                    span.setAttribute('class', 'stat');
+                    text = document.createTextNode(option.stats.attack + " ");
+                    span.appendChild(text);
+                    stat_td.appendChild(span);
+                    span = document.createElement('span');
+                    if (Number(option.stats.attack) > Number(current_stats["attack"])){
+
+                        span.setAttribute('class', 'variation increment');
+                        text = document.createTextNode(option.stats.attack - current_stats["attack"]);
+
+                    }
+                    else if (Number(option.stats.attack) < Number(current_stats["attack"])){
+                        span.setAttribute('class', 'variation decrement');
+                        text = document.createTextNode(current_stats["attack"] - option.stats.attack);
+
+                    }
+                    else{
+                        span.setAttribute('class', 'variation');
+                        text = document.createTextNode(" ");
+                    }
+                    span.appendChild(text);
+                    stat_td.appendChild(span);
+                    stat_tr.appendChild(stat_td);
+                    stat_table.appendChild(stat_tr);
+
+                    // defense
+                    stat_tr = document.createElement('tr');
+                    stat_td = document.createElement('td');
+                    stat_td.setAttribute('class', 'label');
+                    text = document.createTextNode('DEF');
+                    stat_td.appendChild(text);
+                    stat_tr.appendChild(stat_td);
+                    stat_td = document.createElement('td');
+                    stat_td.setAttribute('class', 'value');
+                    span = document.createElement('span');
+                    span.setAttribute('class', 'stat');
+                    text = document.createTextNode(option.stats.defense + " ");
+                    span.appendChild(text);
+                    stat_td.appendChild(span);
+                    span = document.createElement('span');
+                    if (Number(option.stats.defense) > Number(current_stats["defense"])){
+
+                        span.setAttribute('class', 'variation increment');
+                        text = document.createTextNode(option.stats.defense - current_stats["defense"]);
+
+                    }
+                    else if (Number(option.stats.defense) < Number(current_stats["defense"])){
+                        span.setAttribute('class', 'variation decrement');
+                        text = document.createTextNode(current_stats["defense"] - option.stats.defense);
+
+                    }
+                    else{
+                        span.setAttribute('class', 'variation');
+                        text = document.createTextNode(" ");
+                    }
+                    span.appendChild(text);
+                    stat_td.appendChild(span);
+                    stat_tr.appendChild(stat_td);
+                    stat_table.appendChild(stat_tr);
+
+                    // hp
+                    stat_tr = document.createElement('tr');
+                    stat_td = document.createElement('td');
+                    stat_td.setAttribute('class', 'label');
+                    text = document.createTextNode('HP ');
+                    stat_td.appendChild(text);
+                    stat_tr.appendChild(stat_td);
+                    stat_td = document.createElement('td');
+                    stat_td.setAttribute('class', 'value');
+                    span = document.createElement('span');
+                    span.setAttribute('class', 'stat');
+                    text = document.createTextNode(option.stats.hp + " ");
+                    span.appendChild(text);
+                    stat_td.appendChild(span);
+                    span = document.createElement('span');
+                    if (Number(option.stats.hp) > Number(current_stats["hp"])){
+
+                        span.setAttribute('class', 'variation increment');
+                        text = document.createTextNode(option.stats.hp - current_stats["hp"]);
+
+                    }
+                    else if (Number(option.stats.hp) < Number(current_stats["hp"])){
+                        span.setAttribute('class', 'variation decrement');
+                        text = document.createTextNode(current_stats["hp"] - option.stats.hp);
+
+                    }
+                    else{
+                        span.setAttribute('class', 'variation');
+                        text = document.createTextNode(" ");
+                    }
+                    span.appendChild(text);
+                    stat_td.appendChild(span);
+                    stat_tr.appendChild(stat_td);
+                    stat_table.appendChild(stat_tr);
+
+                    // speed
+                    stat_tr = document.createElement('tr');
+                    stat_td = document.createElement('td');
+                    stat_td.setAttribute('class', 'label');
+                    text = document.createTextNode('SPD');
+                    stat_td.appendChild(text);
+                    stat_tr.appendChild(stat_td);
+                    stat_td = document.createElement('td');
+                    stat_td.setAttribute('class', 'value');
+                    span = document.createElement('span');
+                    span.setAttribute('class', 'stat');
+                    text = document.createTextNode(option.stats.speed + " ");
+                    span.appendChild(text);
+                    stat_td.appendChild(span);
+                    span = document.createElement('span');
+                    if (Number(option.stats.speed) > Number(current_stats["speed"])){
+
+                        span.setAttribute('class', 'variation increment');
+                        text = document.createTextNode(option.stats.speed - current_stats["speed"]);
+
+                    }
+                    else if (Number(option.stats.speed) < Number(current_stats["speed"])){
+                        span.setAttribute('class', 'variation decrement');
+                        text = document.createTextNode(current_stats["speed"] - option.stats.speed);
+
+                    }
+                    else{
+                        span.setAttribute('class', 'variation');
+                        text = document.createTextNode(" ");
+                    }
+                    span.appendChild(text);
+                    stat_td.appendChild(span);
+                    stat_tr.appendChild(stat_td);
+                    stat_table.appendChild(stat_tr);
+
+                    // crit_rate
+                    stat_tr = document.createElement('tr');
+                    stat_td = document.createElement('td');
+                    stat_td.setAttribute('class', 'label');
+                    text = document.createTextNode('CRR');
+                    stat_td.appendChild(text);
+                    stat_tr.appendChild(stat_td);
+                    stat_td = document.createElement('td');
+                    stat_td.setAttribute('class', 'value');
+                    span = document.createElement('span');
+                    span.setAttribute('class', 'stat');
+                    text = document.createTextNode(option.stats.crit_rate + "%");
+                    span.appendChild(text);
+                    stat_td.appendChild(span);
+                    span = document.createElement('span');
+                    if (Number(option.stats.crit_rate) > Number(current_stats["crit_rate"])){
+
+                        span.setAttribute('class', 'variation increment');
+                        text = document.createTextNode(option.stats.crit_rate - current_stats["crit_rate"]);
+
+                    }
+                    else if (Number(option.stats.crit_rate) < Number(current_stats["crit_rate"])){
+                        span.setAttribute('class', 'variation decrement');
+                        text = document.createTextNode(current_stats["crit_rate"] - option.stats.crit_rate);
+
+                    }
+                    else{
+                        span.setAttribute('class', 'variation');
+                        text = document.createTextNode(" ");
+                    }
+                    span.appendChild(text);
+                    stat_td.appendChild(span);
+                    stat_tr.appendChild(stat_td);
+                    stat_table.appendChild(stat_tr);
+
+                    // crit_damage
+                    stat_tr = document.createElement('tr');
+                    stat_td = document.createElement('td');
+                    stat_td.setAttribute('class', 'label');
+                    text = document.createTextNode('CRD');
+                    stat_td.appendChild(text);
+                    stat_tr.appendChild(stat_td);
+                    stat_td = document.createElement('td');
+                    stat_td.setAttribute('class', 'value');
+                    span = document.createElement('span');
+                    span.setAttribute('class', 'stat');
+                    text = document.createTextNode(option.stats.crit_damage + "%");
+                    span.appendChild(text);
+                    stat_td.appendChild(span);
+                    span = document.createElement('span');
+                    if (Number(option.stats.crit_damage) > Number(current_stats["crit_damage"])){
+
+                        span.setAttribute('class', 'variation increment');
+                        text = document.createTextNode(option.stats.crit_damage - current_stats["crit_damage"]);
+
+                    }
+                    else if (Number(option.stats.crit_damage) < Number(current_stats["crit_damage"])){
+                        span.setAttribute('class', 'variation decrement');
+                        text = document.createTextNode(current_stats["crit_damage"] - option.stats.crit_damage);
+
+                    }
+                    else{
+                        span.setAttribute('class', 'variation');
+                        text = document.createTextNode(" ");
+                    }
+                    span.appendChild(text);
+                    stat_td.appendChild(span);
+                    stat_tr.appendChild(stat_td);
+                    stat_table.appendChild(stat_tr);
+
+                    // accuracy
+                    stat_tr = document.createElement('tr');
+                    stat_td = document.createElement('td');
+                    stat_td.setAttribute('class', 'label');
+                    text = document.createTextNode('ACC');
+                    stat_td.appendChild(text);
+                    stat_tr.appendChild(stat_td);
+                    stat_td = document.createElement('td');
+                    stat_td.setAttribute('class', 'value');
+                    span = document.createElement('span');
+                    span.setAttribute('class', 'stat');
+                    text = document.createTextNode(option.stats.accuracy + "%");
+                    span.appendChild(text);
+                    stat_td.appendChild(span);
+                    span = document.createElement('span');
+                    if (Number(option.stats.accuracy) > Number(current_stats["accuracy"])){
+
+                        span.setAttribute('class', 'variation increment');
+                        text = document.createTextNode(option.stats.accuracy - current_stats["accuracy"]);
+
+                    }
+                    else if (Number(option.stats.accuracy) < Number(current_stats["accuracy"])){
+                        span.setAttribute('class', 'variation decrement');
+                        text = document.createTextNode(current_stats["accuracy"] - option.stats.accuracy);
+
+                    }
+                    else{
+                        span.setAttribute('class', 'variation');
+                        text = document.createTextNode(" ");
+                    }
+                    span.appendChild(text);
+                    stat_td.appendChild(span);
+                    stat_tr.appendChild(stat_td);
+                    stat_table.appendChild(stat_tr);
+
+                    // resistance
+                    stat_tr = document.createElement('tr');
+                    stat_td = document.createElement('td');
+                    stat_td.setAttribute('class', 'label');
+                    text = document.createTextNode('RES');
+                    stat_td.appendChild(text);
+                    stat_tr.appendChild(stat_td);
+                    stat_td = document.createElement('td');
+                    stat_td.setAttribute('class', 'value');
+                    span = document.createElement('span');
+                    span.setAttribute('class', 'stat');
+                    text = document.createTextNode(option.stats.resistance + "%");
+                    span.appendChild(text);
+                    stat_td.appendChild(span);
+                    span = document.createElement('span');
+                    if (Number(option.stats.resistance) > Number(current_stats["resistance"])){
+
+                        span.setAttribute('class', 'variation increment');
+                        text = document.createTextNode(option.stats.resistance - current_stats["resistance"]);
+
+                    }
+                    else if (Number(option.stats.resistance) < Number(current_stats["resistance"])){
+                        span.setAttribute('class', 'variation decrement');
+                        text = document.createTextNode(current_stats["resistance"] - option.stats.resistance);
+
+                    }
+                    else{
+                        span.setAttribute('class', 'variation');
+                        text = document.createTextNode(" ");
+                    }
+                    span.appendChild(text);
+                    stat_td.appendChild(span);
+                    stat_tr.appendChild(stat_td);
+                    stat_table.appendChild(stat_tr);
+
+                    // ehp
+                    stat_tr = document.createElement('tr');
+                    stat_td = document.createElement('td');
+                    stat_td.setAttribute('class', 'label');
+                    text = document.createTextNode('EHP');
+                    stat_td.appendChild(text);
+                    stat_tr.appendChild(stat_td);
+                    stat_td = document.createElement('td');
+                    stat_td.setAttribute('class', 'value');
+                    span = document.createElement('span');
+                    span.setAttribute('class', 'stat');
+                    text = document.createTextNode(option.stats.ehp + " ");
+                    span.appendChild(text);
+                    stat_td.appendChild(span);
+                    span = document.createElement('span');
+                    if (Number(option.stats.ehp) > Number(current_stats["ehp"])){
+
+                        span.setAttribute('class', 'variation increment');
+                        text = document.createTextNode(option.stats.ehp - current_stats["ehp"]);
+
+                    }
+                    else if (Number(option.stats.ehp) < Number(current_stats["ehp"])){
+                        span.setAttribute('class', 'variation decrement');
+                        text = document.createTextNode(current_stats["ehp"] - option.stats.ehp);
+
+                    }
+                    else{
+                        span.setAttribute('class', 'variation');
+                        text = document.createTextNode(" ");
+                    }
+                    span.appendChild(text);
+                    stat_td.appendChild(span);
+                    stat_tr.appendChild(stat_td);
+                    stat_table.appendChild(stat_tr);
+
+                    // dmg
+                    stat_tr = document.createElement('tr');
+                    stat_td = document.createElement('td');
+                    stat_td.setAttribute('class', 'label');
+                    text = document.createTextNode('DMG');
+                    stat_td.appendChild(text);
+                    stat_tr.appendChild(stat_td);
+                    stat_td = document.createElement('td');
+                    stat_td.setAttribute('class', 'value');
+                    span = document.createElement('span');
+                    span.setAttribute('class', 'stat');
+                    text = document.createTextNode(option.stats.dmg + " ");
+                    span.appendChild(text);
+                    stat_td.appendChild(span);
+                    span = document.createElement('span');
+                    if (Number(option.stats.dmg) > Number(current_stats["dmg"])){
+
+                        span.setAttribute('class', 'variation increment');
+                        text = document.createTextNode(option.stats.dmg - current_stats["dmg"]);
+
+                    }
+                    else if (Number(option.stats.dmg) < Number(current_stats["dmg"])){
+                        span.setAttribute('class', 'variation decrement');
+                        text = document.createTextNode(current_stats["dmg"] - option.stats.dmg);
+
+                    }
+                    else{
+                        span.setAttribute('class', 'variation');
+                        text = document.createTextNode(" ");
+                    }
+                    span.appendChild(text);
+                    stat_td.appendChild(span);
+                    stat_tr.appendChild(stat_td);
+                    stat_table.appendChild(stat_tr);
+
+                    // Add to the main table
+                    td.appendChild(stat_table);
+                    tr.appendChild(td);
+
+                    // Runes td
+                    td = document.createElement('td');
+                    td.setAttribute('class', 'runes');
+                    td.innerHTML += option.runes[6 - 1].html;
+                    td.innerHTML += option.runes[1 - 1].html;
+                    td.innerHTML += option.runes[2 - 1].html;
+                    td.innerHTML += "<br/>";
+                    td.innerHTML += option.runes[5 - 1].html;
+                    td.innerHTML += option.runes[4 - 1].html;
+                    td.innerHTML += option.runes[3 - 1].html;
+
+                    // Add the table
+                    tr.appendChild(td);
+                    container.appendChild(tr);
+
+                    // Make the section visible
+                    document.getElementById('results').style.display = 'block';
+
+                }
+            }
+
+            /**
+             * Displays a pop-up message.
+             *
+             * @param title Message title.
+             * @param text Message content.
+             */
+            function showMessage(title, text){
+                document.getElementById('message_title').innerHTML = title;
+                document.getElementById('message_text').innerHTML = text;
+                document.getElementById('message_container').style.display = 'block';
+            }
+
+            /**
+             * Closes the message pop-up.
+             */
+            function closeMessage(){
+                document.getElementById('message_container').style.display = 'none';
+            }
+        </script>
+    </head>
+    <body>
+<?php
+        include __DIR__ . "/inc/header.php";
+?>
+        <section id='monster' class='content'>
+            <h2>
+                Optimize <?=$page->unit->title?>
+            </h2>
+            <article id='profile'>
+                <div id='monster_info'>
+                    <div class='monster_panel'>
+                        <?=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'));
+                    $datediff = floor($datediff / (60 * 60 * 24));
+?>
+                    <div id='monster_details'>
+<?php
+                        if (sizeof($page->unit->teams) > 0){
+?>
+                            <div id='teams'>
+                                <h4>
+                                    Teams:
+                                </h4>
+                                <ul>
+<?php
+                                    foreach($page->unit->teams as $team){
+?>
+                                        <li>
+                                            <a target='blank' href='/<?=$UID?>/teams/<?=$team->id?>'>
+                                                <?=$team->name?>
+                                            </a>
+                                        </li>
+<?php
+                                    }
+?>
+                                </ul>
+                            </div>
+<?php
+                        }
+?>
+                    </div>
+                </div>
+                <table id='stats'>
+                    <tr>
+                        <th>
+                            Stats
+                        </th>
+                        <th>
+                            Base
+                        </th>
+                        <th>
+                            Runes
+                        </th>
+                        <th>
+                            Artifacts
+                        </th>
+                        <th>
+                            Buildings
+                        </th>
+                        <th>
+                            Total
+                        </th>
+                    </tr>
+                    <tr>
+                        <th>
+                            ATK
+                        </th>
+                        <td class='base'>
+                            <?=$page->unit->attack?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->rune_attack?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->artifact_attack?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->building_attack?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                        <td class='total'>
+                            <?=$page->unit->total_attack?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                    </tr>
+                    <tr>
+                        <th>
+                            DEF
+                        </th>
+                        <td class='base'>
+                            <?=$page->unit->defense?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->rune_defense?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->artifact_defense?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->building_defense?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                        <td class='total'>
+                            <?=$page->unit->total_defense?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                    </tr>
+                    <tr>
+                        <th>
+                            HP
+                        </th>
+                        <td class='base'>
+                            <?=$page->unit->hp?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->rune_hp?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->artifact_hp?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->building_hp?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                        <td class='total'>
+                            <?=$page->unit->total_hp?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                    </tr>
+                    <tr>
+                        <th>
+                            SPD
+                        </th>
+                        <td class='base'>
+                            <?=$page->unit->speed?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->rune_speed?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->artifact_speed?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->building_speed?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                        <td class='total'>
+                            <?=$page->unit->total_speed?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                    </tr>
+                    <tr>
+                        <th>
+                            CRR
+                        </th>
+                        <td class='base'>
+                            <?=$page->unit->crit_rate?>
+                            <span class='percent'>%</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->rune_crit_rate?>
+                            <span class='percent'>%</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->artifact_crit_rate?>
+                            <span class='percent'>%</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->building_crit_rate?>
+                            <span class='percent'>%</span>
+                        </td>
+                        <td class='total'>
+                            <?=$page->unit->total_crit_rate?>
+                            <span class='percent'>%</span>
+                        </td>
+                    </tr>
+                    <tr>
+                        <th>
+                            CRD
+                        </th>
+                        <td class='base'>
+                            <?=$page->unit->crit_damage?>
+                            <span class='percent'>%</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->rune_crit_damage?>
+                            <span class='percent'>%</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->artifact_crit_damage?>
+                            <span class='percent'>%</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->building_crit_damage?>
+                            <span class='percent'>%</span>
+                        </td>
+                        <td class='total'>
+                            <?=$page->unit->total_crit_damage?>
+                            <span class='percent'>%</span>
+                        </td>
+                    </tr>
+                    <tr>
+                        <th>
+                            ACC
+                        </th>
+                        <td class='base'>
+                            <?=$page->unit->accuracy?>
+                            <span class='percent'>%</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->rune_accuracy?>
+                            <span class='percent'>%</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->artifact_accuracy?>
+                            <span class='percent'>%</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->building_accuracy?>
+                            <span class='percent'>%</span>
+                        </td>
+                        <td class='total'>
+                            <?=$page->unit->total_accuracy?>
+                            <span class='percent'>%</span>
+                        </td>
+                    </tr>
+                    <tr>
+                        <th>
+                            RES
+                        </th>
+                        <td class='base'>
+                            <?=$page->unit->resistance?>
+                            <span class='percent'>%</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->rune_resistance?>
+                            <span class='percent'>%</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->artifact_resistance?>
+                            <span class='percent'>%</span>
+                        </td>
+                        <td class='extra'>
+                            +<?=$page->unit->building_resistance?>
+                            <span class='percent'>%</span>
+                        </td>
+                        <td class='total'>
+                            <?=$page->unit->total_resistance?>
+                            <span class='percent'>%</span>
+                        </td>
+                    </tr>
+                    <tr>
+                        <th colspan='5'>
+                            Effective HP
+                        </th>
+                        <td class='total'>
+                            <?=$page->unit->effective_hp?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                    </tr>
+                    <tr>
+                        <th colspan='5'>
+                            Effective DMG
+                        </th>
+                        <td class='total'>
+                            <?=$page->unit->effective_dmg?>
+                            <span class='percent'>&nbsp;</span>
+                        </td>
+                    </tr>
+                </table>
+                <div id='artifacts'>
+                    <?=HTML::artifact_table($page->unit->artifact_type)?>
+                    <?=HTML::artifact_table($page->unit->artifact_element)?>
+                </div>
+            </article> <!-- #profile -->
+            <article id='current_runes'>
+<?php
+                $show_list = [6, 1, 2, 5, 4, 3];
+                //foreach ($page->unit->rune as $rune){
+                foreach ($show_list as $i){
+                    $rune = $page->unit->rune[$i - 1];
+                    $total_runes ++;
+?>
+                    <?=HTML::rune_table($rune)?>
+<?php
+                    if ($i == 2){
+?>
+                        <br/>
+<?php
+                    }
+                }
+?>
+            </article>
+        </section> <!-- runes -->
+        <section id='parameters' class='content'>
+            <h2>
+                Optimization parameters
+            </h2>
+            <article>
+                <table id='parameters'>
+                    <tr>
+                        <td class='name'>
+                            Rune sets
+                        </td>
+                        <td class='value'>
+<?php
+                            $reflect = new ReflectionClass("RUNE_SET_ID");
+?>
+                            <select id='set_1'>
+<?php
+                            foreach($reflect->getConstants() as $name => $id){
+                                $selected = "";
+                                if ($id == $sets[0]){
+                                    $selected = "selected='selected'";
+                                }
+?>
+                                <option value='<?=$id?>' <?=$selected?>><?=substr($name, 0, 7)?></option>
+<?php
+                            }
+?>
+                            </select>
+                            <select id='set_2'>
+<?php
+                            foreach($reflect->getConstants() as $name => $id){
+                                $selected = "";
+                                if ($id == $sets[1]){
+                                    $selected = "selected='selected'";
+                                }
+?>
+                                <option value='<?=$id?>' <?=$selected?>><?=substr($name, 0, 7)?></option>
+<?php
+                            }
+?>
+                            </select>
+                            <select id='set_3'>
+                                <option value='' <?=$selected?>></option>
+<?php
+                            foreach($reflect->getConstants() as $name => $id){
+                                $selected = "";
+                                if ($id == $sets[2]){
+                                    $selected = "selected='selected'";
+                                }
+?>
+                                <option value='<?=$id?>' <?=$selected?>><?=substr($name, 0, 7)?></option>
+<?php
+                            }
+?>
+                            </select>
+                        </td>
+                        <td class='empty'>
+                        </td>
+                        <td class='name'>
+                            Min HP
+                        </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>
+                            <a title='Remove filter' class='filter_control a_button' onclick='removeFilter("hp");'>
+                                <img alt='Remove filter' src='<?=URL::IMG["ICON"]?>nok.png'/>
+                            </a>
+                            <a title='Reset filter' class='filter_control a_button' onclick='resetFilter("hp");'>
+                                <img alt='Reset filter' src='<?=URL::IMG["ICON"]?>edit.png'/>
+                            </a>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class='name'>
+                            Even slots
+                        </td>
+                        <td class='value'>
+                            <select id='slot_2'>
+                                <option value='<?=RUNE_STAT_ID::HP_P?>' <?=($mains[0] == RUNE_STAT_ID::HP_P) ? ("selected='selected'") : ("");?>>HP%</option>
+                                <option value='<?=RUNE_STAT_ID::ATK_P?>' <?=($mains[0] == RUNE_STAT_ID::ATK_P) ? ("selected='selected'") : ("");?>>ATK%</option>
+                                <option value='<?=RUNE_STAT_ID::DEF_P?>' <?=($mains[0] == RUNE_STAT_ID::DEF_P) ? ("selected='selected'") : ("");?>>DEF%</option>
+                                <option value='<?=RUNE_STAT_ID::SPD?>' <?=($mains[0] == RUNE_STAT_ID::SPD) ? ("selected='selected'") : ("");?>>SPD</option>
+                            </select>
+                            <select id='slot_4'>
+                                <option value='<?=RUNE_STAT_ID::HP_P?>' <?=($mains[1] == RUNE_STAT_ID::HP_P) ? ("selected='selected'") : ("");?>>HP%</option>
+                                <option value='<?=RUNE_STAT_ID::ATK_P?>' <?=($mains[1] == RUNE_STAT_ID::ATK_P) ? ("selected='selected'") : ("");?>>ATK%</option>
+                                <option value='<?=RUNE_STAT_ID::DEF_P?>' <?=($mains[1] == RUNE_STAT_ID::DEF_P) ? ("selected='selected'") : ("");?>>DEF%</option>
+                                <option value='<?=RUNE_STAT_ID::CRR?>' <?=($mains[1] == RUNE_STAT_ID::CRR) ? ("selected='selected'") : ("");?>>CRR</option>
+                                <option value='<?=RUNE_STAT_ID::CRR?>' <?=($mains[1] == RUNE_STAT_ID::CRD) ? ("selected='selected'") : ("");?>>CRD</option>
+                            </select>
+                            <select id='slot_6'>
+                                <option value='<?=RUNE_STAT_ID::HP_P?>' <?=($mains[2] == RUNE_STAT_ID::HP_P) ? ("selected='selected'") : ("");?>>HP%</option>
+                                <option value='<?=RUNE_STAT_ID::ATK_P?>' <?=($mains[2] == RUNE_STAT_ID::ATK_P) ? ("selected='selected'") : ("");?>>ATK%</option>
+                                <option value='<?=RUNE_STAT_ID::DEF_P?>' <?=($mains[2] == RUNE_STAT_ID::DEF_P) ? ("selected='selected'") : ("");?>>DEF%</option>
+                                <option value='<?=RUNE_STAT_ID::ACC?>' <?=($mains[2] == RUNE_STAT_ID::ACC) ? ("selected='selected'") : ("");?>>ACC</option>
+                                <option value='<?=RUNE_STAT_ID::RES?>' <?=($mains[2] == RUNE_STAT_ID::RES) ? ("selected='selected'") : ("");?>>RES</option>
+                            </select>
+                        </td>
+                        <td class='empty'>
+                        </td>
+                        <td class='name'>
+                            Min ATK
+                        </td>
+                        <td class='value'>
+                            <input id='min_attack' type='range' value='<?=$page->unit->total_attack?>' min='<?=$page->unit->attack?>' max='4000' oninput='updateMinValue("attack", this);'>
+                            <input type='number' id='lbl_min_attack' value='<?=$page->unit->total_attack?>' min='<?=$page->unit->attack?>' max='4000' onChange='updateMinValue("attack", this);'></input><span class='slider_percent'> </span>
+                            <a title='Remove filter' class='filter_control a_button' onclick='removeFilter("attack");'>
+                                <img alt='Remove filter' src='<?=URL::IMG["ICON"]?>nok.png'/>
+                            </a>
+                            <a title='Reset filter' class='filter_control a_button' onclick='resetFilter("attack");'>
+                                <img alt='Reset filter' src='<?=URL::IMG["ICON"]?>edit.png'/>
+                            </a>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class='name'>
+                            Use runes from
+                        </td>
+                        <td class='value'>
+                            <select id='rune_source'>
+                                <option value='0'>Storage only</option>
+                                <option value='1'>Units in no teams</option>
+                                <option value='2'>Units in teams with 0 score</option>
+                                <option value='3'>Units with lower overall score</option>
+                                <option value='4'>All runes</option>
+                            </select>
+                        </td>
+                        <td class='empty'>
+                        </td>
+                        <td class='name'>
+                            Min DEF
+                        </td>
+                        <td class='value'>
+                            <input id='min_defense' type='range' value='<?=$page->unit->total_defense?>' min='<?=$page->unit->defense?>' max='4000' oninput='updateMinValue("defense", this);'>
+                            <input type='number' id='lbl_min_defense' value='<?=$page->unit->total_defense?>' min='<?=$page->unit->defense?>' max='4000' onChange='updateMinValue("defense", this);'></input><span class='slider_percent'> </span>
+                            <a title='Remove filter' class='filter_control a_button' onclick='removeFilter("defense");'>
+                                <img alt='Remove filter' src='<?=URL::IMG["ICON"]?>nok.png'/>
+                            </a>
+                            <a title='Reset filter' class='filter_control a_button' onclick='resetFilter("defense");'>
+                                <img alt='Reset filter' src='<?=URL::IMG["ICON"]?>edit.png'/>
+                            </a>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class='name'>
+                            Tune optimizer for
+                        </td>
+                        <td class='value'>
+                            <select id='tuning'>
+                                <option value='0'>Rune current levels</option>
+                                <option value='1'>+12</option>
+                                <option value='2'>2/4/6 +15, 1/3/5 + 12</option>
+                                <option value='3'>+15</option>
+                            </select>
+                        </td>
+                        <td class='empty'>
+                        </td>
+                        <td class='name'>
+                            Min SPD
+                        </td>
+                        <td class='value'>
+                            <input id='min_speed' type='range' value='<?=$page->unit->total_speed?>' min='<?=$page->unit->speed?>' max='400' oninput='updateMinValue("speed", this);'>
+                            <input type='number' id='lbl_min_speed' value='<?=$page->unit->total_speed?>' min='<?=$page->unit->speed?>' max='400' onChange='updateMinValue("speed", this);'></input><span class='slider_percent'> </span>
+                            <a title='Remove filter' class='filter_control a_button' onclick='removeFilter("speed");'>
+                                <img alt='Remove filter' src='<?=URL::IMG["ICON"]?>nok.png'/>
+                            </a>
+                            <a title='Reset filter' class='filter_control a_button' onclick='resetFilter("speed");'>
+                                <img alt='Reset filter' src='<?=URL::IMG["ICON"]?>edit.png'/>
+                            </a>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class='empty'>
+                            Max. combinations
+                        </td>
+                        <td class='empty'>
+                            <input id='limit' type='number' value='10' max='100' min='1'>
+                        </td>
+                        <td class='empty'>
+                        </td>
+                        <td class='name'>
+                            Min CRR
+                        </td>
+                        <td class='value'>
+                            <input id='min_crit_rate' type='range' value='<?=$page->unit->total_crit_rate?>' min='<?=$page->unit->crit_rate?>' max='100' oninput='updateMinValue("crit_rate", this);'>
+                            <input type='number' id='lbl_min_crit_rate' value='<?=$page->unit->total_crit_rate?>' min='<?=$page->unit->crit_rate?>' max='100' onChange='updateMinValue("crit_rate", this);'></input><span class='slider_percent'>%</span>
+                            <a title='Remove filter' class='filter_control a_button' onclick='removeFilter("crit_rate");'>
+                                <img alt='Remove filter' src='<?=URL::IMG["ICON"]?>nok.png'/>
+                            </a>
+                            <a title='Reset filter' class='filter_control a_button' onclick='resetFilter("crit_rate");'>
+                                <img alt='Reset filter' src='<?=URL::IMG["ICON"]?>edit.png'/>
+                            </a>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class='empty'>
+                        </td>
+                        <td class='empty'>
+                        </td>
+                        <td class='empty'>
+                        </td>
+                        <td class='name'>
+                            Min CRD
+                        </td>
+                        <td class='value'>
+                            <input id='min_crit_damage' type='range' value='<?=$page->unit->total_crit_damage?>' min='<?=$page->unit->crit_damage?>' max='400' oninput='updateMinValue("crit_damage", this);'>
+                            <input type='number' id='lbl_min_crit_damage' value='<?=$page->unit->total_crit_damage?>' min='<?=$page->unit->crit_damage?>' max='400' onChange='updateMinValue("crit_damage", this);'></input><span class='slider_percent'>%</span>
+                            <a title='Remove filter' class='filter_control a_button' onclick='removeFilter("crit_damage");'>
+                                <img alt='Remove filter' src='<?=URL::IMG["ICON"]?>nok.png'/>
+                            </a>
+                            <a title='Reset filter' class='filter_control a_button' onclick='resetFilter("crit_damage");'>
+                                <img alt='Reset filter' src='<?=URL::IMG["ICON"]?>edit.png'/>
+                            </a>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class='empty'>
+                        </td>
+                        <td class='empty'>
+                        </td>
+                        <td class='empty'>
+                        </td>
+                        <td class='name'>
+                            Min ACC
+                        </td>
+                        <td class='value'>
+                            <input id='min_accuracy' type='range' value='<?=$page->unit->total_accuracy?>' min='<?=$page->unit->accuracy?>' max='85' oninput='updateMinValue("accuracy", this);'>
+                            <input type='number' id='lbl_min_accuracy' value='<?=$page->unit->total_accuracy?>' min='<?=$page->unit->accuracy?>' max='85' onChange='updateMinValue("accuracy", this);'></input><span class='slider_percent'>%</span>
+                            <a title='Remove filter' class='filter_control a_button' onclick='removeFilter("accuracy");'>
+                                <img alt='Remove filter' src='<?=URL::IMG["ICON"]?>nok.png'/>
+                            </a>
+                            <a title='Reset filter' class='filter_control a_button' onclick='resetFilter("accuracy");'>
+                                <img alt='Reset filter' src='<?=URL::IMG["ICON"]?>edit.png'/>
+                            </a>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class='empty'>
+                        </td>
+                        <td class='empty'>
+                        </td>
+                        <td class='empty'>
+                        </td>
+                        <td class='name'>
+                            Min RES
+                        </td>
+                        <td class='value'>
+                            <input id='min_resistance' type='range' value='<?=$page->unit->total_resistance?>' min='<?=$page->unit->resistance?>' max='85' oninput='updateMinValue("resistance", this);'>
+                            <input type='number' id='lbl_min_resistance' value='<?=$page->unit->total_resistance?>' min='<?=$page->unit->resistance?>' max='85' onChange='updateMinValue("resistance", this);'></input><span class='slider_percent'>%</span>
+                            <a title='Remove filter' class='filter_control a_button' onclick='removeFilter("resistance");'>
+                                <img alt='Remove filter' src='<?=URL::IMG["ICON"]?>nok.png'/>
+                            </a>
+                            <a title='Reset filter' class='filter_control a_button' onclick='resetFilter("resistance");'>
+                                <img alt='Reset filter' src='<?=URL::IMG["ICON"]?>edit.png'/>
+                            </a>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class='empty'>
+                        </td>
+                        <td class='empty'>
+                        </td>
+                        <td class='empty'>
+                        </td>
+                        <td class='name'>
+                            Min EHP
+                        </td>
+                        <td class='value'>
+                            <input id='min_ehp' type='range' value='<?=$page->unit->effective_hp?>' min='0' max='200000' oninput='updateMinValue("ehp", this);'>
+                            <input type='number' id='lbl_min_ehp' value='<?=$page->unit->effective_hp?>' min='0' max='200000' onChange='updateMinValue("ehp", this);'></input><span class='slider_percent'> </span>
+                            <a title='Remove filter' class='filter_control a_button' onclick='removeFilter("ehp");'>
+                                <img alt='Remove filter' src='<?=URL::IMG["ICON"]?>nok.png'/>
+                            </a>
+                            <a title='Reset filter' class='filter_control a_button' onclick='resetFilter("ehp");'>
+                                <img alt='Reset filter' src='<?=URL::IMG["ICON"]?>edit.png'/>
+                            </a>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class='action'>
+                            <input id='apply' type='button' value='Apply' onClick='apply();'/>
+                        </td>
+                        <td class='empty'>
+                            <img id='wait' src='<?=URL::IMG["ICON"] . "wait.gif";?>'>
+                        </td>
+                        <td class='empty'>
+                        </td>
+                        <td class='name'>
+                            Min DMG
+                        </td>
+                        <td class='value'>
+                            <input id='min_dmg' type='range' value='<?=$page->unit->effective_dmg?>' min='0' max='7000' oninput='updateMinValue("dmg", this);'>
+                            <input type='number' id='lbl_min_dmg' value='<?=$page->unit->effective_dmg?>' min='0' max='7000' onChange='updateMinValue("dmg", this);'></input><span class='slider_percent'> </span>
+                            <a title='Remove filter' class='filter_control a_button' onclick='removeFilter("dmg");'>
+                                <img alt='Remove filter' src='<?=URL::IMG["ICON"]?>nok.png'/>
+                            </a>
+                            <a title='Reset filter' class='filter_control a_button' onclick='resetFilter("dmg");'>
+                                <img alt='Reset filter' src='<?=URL::IMG["ICON"]?>edit.png'/>
+                            </a>
+                        </td>
+                    </tr>
+
+
+
+
+                </table>
+            </article>
+        </section>
+        <section id='results' class='content'>
+            <h2>
+                Optimization results
+            </h2>
+            <article>
+                <div id='results_info'>
+                    <span id='num_results'></span> results (<span id='num_skipped'></span> skipped) in <span id='num_seconds'></span> seconds
+                </div>
+                <table id='table_optimizations'>
+                    <tbody id='optimizations'>
+                    </tbody>
+                </table>
+            </article>
+        </section>
+        <div id='message_container'>
+            <section class='content'>
+                <h2 id='message_title'>
+                    Message
+                </h2>
+                <article>
+                    <p id='message_text'>
+                        Message Text
+                    </p>
+                </article>
+                <input type='button' value='OK' id='message_button' onclick='closeMessage();'/>
+            </section>
+        </div>
+
+<?php
+        include __DIR__ . "/inc/footer.php";
+?>
+    </body>
+</html>

+ 2 - 1
install_data/install_data.sql

@@ -331,7 +331,8 @@ CREATE TABLE team(
     area_type INT NOT NULL REFERENCES k_area_type(id),
     area INT REFERENCES k_area(id),
     stage INT,
-    difficulty INT REFERENCES k_difficulty(id)
+    difficulty INT REFERENCES k_difficulty(id),
+    score INT NOT NULL DEFAULT 0
 );
 CREATE TABLE building(
     -- No primary key, can have multiple towers

+ 316 - 0
public/css/optimizer.css

@@ -0,0 +1,316 @@
+table#teams{
+    width: 60%;
+    border-collapse: collapse;
+    border: 0.1em solid #000000;
+    display: inline-block;
+    vertical-align: top;
+}
+
+table#teams th{
+    border-top: 0.1em solid #000000;
+    border-bottom: 0.1em solid #000000;
+}
+
+table#teams td{
+    border-bottom: 0.1em solid #000000;
+    text-align: left;
+    padding: 0.2em;
+}
+
+table#teams td.odd{
+    background-color: #00000033;
+}
+
+table#teams td.team{
+    font-weight: bold;
+}
+
+table#teams td.description{
+    font-size: 90%;
+}
+
+
+table#teams td.units{
+    width: 12em;
+}
+
+table#teams td.units hr.frontline{
+    display: block;
+    width: 9em;
+    margin: -0.6em 0 0.2em 0;
+    color: #666666;
+}
+
+table#teams td.units div.monster_panel{
+    font-size: 25%;
+    margin-bottom: -2em;
+}
+
+table#teams td.units div.monster_panel img.leader{
+    width: 5em;
+    height: 5em;
+    display: block;
+    position: absolute;
+    top: -1.7em;
+    left: -0.7em;
+}
+
+div#unit_list{
+    display: inline-block;
+    width: 30%;
+    vertical-align: top;
+    text-align: center
+}
+
+div#unit_list table{
+    border-collapse: collapse;
+    border: 0.1em solid #000000;
+}
+
+div#unit_list table th{
+    padding: 0.2em 0.4em;
+}
+
+div#unit_list table td{
+    vertical-align: middle;
+    text-align: center;
+    margin: 0;
+}
+
+div#unit_list table td.odd{
+    background-color: #00000033;
+}
+
+div#unit_list table div.monster_panel{
+    font-size: 25%;
+    margin: 2em 0 0 0;
+}
+
+div#unit_list table td.action input[type="button"]{
+    font-size: 90%;
+    padding: 0.1em;
+}
+
+section#monster article#profile div#monster_info{
+    display: inline-block;
+    vertical-align: top;
+    max-width: 10em;
+}
+
+section#monster article#profile div#monster_info div#monster_details{
+    display: block;
+    font-size: 70%;
+}
+
+section#monster article#profile div#monster_info div#monster_details div#teams{
+    text-align: left;
+}
+
+section#monster article#profile div#monster_info div#monster_details div#teams h4{
+    margin: 0.2em auto;
+}
+
+section#monster article#profile table#stats{
+    font-size: 90%;
+    vertical-align: top;
+    display: inline-block;
+    border: 0.1em solid black;
+    color: #ffffff;
+    margin: auto;
+    border-collapse: collapse;
+    border-radius: 0.4em;
+}
+
+section#monster article#profile table#stats{
+    font-size: 90%;
+    vertical-align: top;
+    display: inline-block;
+    border: 0.1em solid black;
+    color: #ffffff;
+    margin: auto;
+    border-collapse: collapse;
+    border-radius: 0.4em;
+}
+
+section#monster article#profile table#stats th{
+    font-weight: bold;
+    background-color: #00000066;
+}
+
+section#monster article#profile table#stats td{
+    font-weight: bold;
+    background-color: #ffffff33;
+    text-align: right;
+    font-family: monospace;
+    font-size: 120%;
+}
+
+section#monster article#profile table#stats td span.percent{
+    margin: 0 0 0 -0.7em;
+    font-size: 80%;
+}
+
+section#monster article#profile table#stats td, th{
+    padding: 0.2em;
+    border: 0.1em solid #000000;
+}
+
+section#monster article#profile table#stats td.extra{
+    color: #aaaaff;
+}
+
+section#monster article#profile table#stats td.total{
+    color: #f2c920;
+    font-weight: bold;
+}
+
+section#monster article#profile div#artifacts{
+    display: inline-block;
+}
+
+section#monster article#profile div#artifacts table.artifact{
+    display: block;
+}
+
+section#parameters article{
+    text-align: left;
+}
+
+section#parameters article table{
+    margin: auto;
+}
+
+section#parameters article input[type="number"]{
+    width: 4em;
+    text-align: right;
+    -moz-appearance: textfield;
+}
+
+section#parameters span.slider_percent{
+    display: inline-block;
+    width: 1em;
+    font-size: 70%;
+}
+
+section#parameters a.filter_control{
+    width: 1em;
+    height: 1em;
+    display: inline-block;
+    vertical-align: bottom;
+}
+section#parameters a.filter_control img{
+    width: 0.8em;
+    height: 0.8em;
+    left: 0.15em;
+    position: relative;
+}
+
+section#parameters article img#wait{
+    width: 2.5em;
+    height: 2.5em;
+    display: none;
+}
+
+section#results{
+    display: none;
+}
+
+section#results div#results_info{
+    text-align: left;
+    font-weight: bold;
+    margin: 2em auto 1em 10%
+}
+
+section#results td.option_stats table.stat_table{
+    border-collapse: collapse;
+    border: 0.2em solid #00000088;
+}
+
+section#results td.option_stats table.stat_table td{
+    border: 0.1em solid #00000033;
+}
+
+section#results td.option_stats table.stat_table td.label{
+    text-align: left;
+    font-family: monospace;
+    font-size: 110%;
+}
+
+section#results td.option_stats table.stat_table td.value{
+    text-align: right;
+    font-family: monospace;
+    font-size: 110%;
+}
+
+section#results td.option_stats table.stat_table td.value span.variation{
+    font-size: 90%;
+    width: 4em;
+    display: inline-block;
+    text-align: left;
+    padding-left: 0.5em;
+    font-weight: bold
+}
+
+section#results td.option_stats table.stat_table td.value span.variation.increment{
+    color: #00ff00;
+}
+
+section#results td.option_stats table.stat_table td.value span.variation.increment::before {
+    content: "▲"; /*"&#9650;";*/
+}
+
+section#results td.option_stats table.stat_table td.value span.variation.decrement{
+    color: #ff0000;
+}
+section#results td.option_stats table.stat_table td.value span.variation.decrement::before {
+    content: "▼"; /*"&#9660;";*/
+}
+
+section#results td.runes table.rune{
+    font-size: 60%;
+}
+
+section#results table#table_optimizations{
+    border-collapse: collapse;
+    margin: auto;
+}
+
+section#results td.option_stats, section#results td.runes{
+    vertical-align: top;
+    padding: 1em 3em;
+    border-top: 0.1em solid #000000cc;
+    border-bottom: 0.1em solid #000000cc;
+}
+section#results td.option_stats{
+    padding-right: 0.5em;
+}
+section#results td.runes{
+    padding-left: 0.5em;
+}
+
+div#message_container{
+    display: none;
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background-color: #00000099;
+}
+
+div#message_container section{
+    position: fixed;
+    top: 30%;
+    left: 20%;
+    right: 20%;
+    bottom: 30%;
+}
+
+div#message_container section p#message_text{
+
+}
+
+div#message_container section input#message_button{
+    position: absolute;
+    bottom: 2em;
+}

+ 5 - 0
public/css/ui.css

@@ -62,6 +62,7 @@
     --input-font-weight: normal;
     --input-padding: 0;
     --input-background: linear-gradient(0deg, rgba(187,132,64,1) 0%, rgba(200,173,120,1) 97%, rgba(209,169,143,1) 100%);
+    --input-background-disabled: linear-gradient(0deg, rgba(93,61,32,1) 0%, rgba(100,86,60,1) 97%, rgba(104,84,71,1) 100%);
     --input-background-unchecked: rgb(96, 71, 40);
     --input-background-checked: rgba(209,169,143,1);
     --input-border: 0;/*0.2em solid var(--ui-color-border);*/
@@ -312,6 +313,10 @@ input, select, a.a_button, textarea{
 }
 a.a_button, input[type=button]{
     cursor: pointer;
+    transition: all .3s ease-in-out;
+}
+input:disabled{
+    background: var(--input-background-disabled);
 }
 input[type=text], textarea{
     text-transform: initial;

binární
public/img/icon/wait.gif