Browse Source

Merge branch 'master' into APIv2

Iñigo Valentin 6 năm trước cách đây
mục cha
commit
d0972ab505
36 tập tin đã thay đổi với 1904 bổ sung282 xóa
  1. 12 1
      application/Constant.php
  2. 4 0
      application/Controller.php
  3. 14 3
      application/action/new_team.php
  4. 1 1
      application/config.php
  5. 2 3
      application/entity/K_Inventory.php
  6. 2 2
      application/entity/K_Leader_Skill.php
  7. 33 25
      application/entity/Rune_Craft.php
  8. 3 0
      application/helper/HTML_Helper.php
  9. 3 3
      application/page/Fusion_Page.php
  10. 10 3
      application/page/Home_Page.php
  11. 2 2
      application/page/Monster_Page.php
  12. 2 2
      application/page/Report_Skillup_Page.php
  13. 0 1
      application/page/Runes_Page.php
  14. 3 0
      application/page/Runs_Page.php
  15. 256 0
      application/page/Stats_Page.php
  16. 2 3
      application/view/fusion.php
  17. 3 3
      application/view/inc/filter_report_runeupgrade.php
  18. 8 8
      application/view/inc/filter_runes.php
  19. 34 30
      application/view/inc/filter_runs.php
  20. 82 0
      application/view/inc/filter_stats.php
  21. 3 0
      application/view/inc/header.php
  22. 1 0
      application/view/monster.php
  23. 164 63
      application/view/monster_info.php
  24. 0 1
      application/view/monsters.php
  25. 1 0
      application/view/report_runecraft.php
  26. 0 1
      application/view/report_skillup.php
  27. 49 2
      application/view/runs.php
  28. 840 0
      application/view/stats.php
  29. 152 43
      application/view/teams.php
  30. 19 6
      public/css/monster.css
  31. 49 51
      public/css/monster_info.css
  32. 25 11
      public/css/runs.css
  33. 51 0
      public/css/stats.css
  34. 55 5
      public/css/teams.css
  35. 19 9
      public/css/ui.css
  36. BIN
      public/img/inventory/000009003.png

+ 12 - 1
application/Constant.php

@@ -1187,7 +1187,7 @@
          * Devilmon.
          */
         const DEVILMON = 151050101;
-        
+
         /**
          * Super Angelmon.
          */
@@ -1797,6 +1797,17 @@
             "NAME" => "",
             "AREA" => null
         ];
+
+        /**
+         * Default values for the rune team filters.
+         */
+        const STATS = [
+            "AREA_TYPE" => null,
+            "AREA" => null,
+            "STAGE" => null,
+            "DIFFICULTY" => null,
+            "INCLUDE_HELPER" => false
+        ];
     };
 
 ?>

+ 4 - 0
application/Controller.php

@@ -203,6 +203,10 @@
                     require_once(PATH::PAGE . "Runs_Page.php");
                     $page = new Runs_Page();
                 }
+                elseif (strtoupper($pars[0]) == "STATS"){
+                    require_once(PATH::PAGE . "Stats_Page.php");
+                    $page = new Stats_Page();
+                }
                 elseif (strtoupper($pars[0]) == "TEAMS"){
                     require_once(PATH::PAGE . "Teams_Page.php");
                     $page = new Teams_Page();

+ 14 - 3
application/action/new_team.php

@@ -40,6 +40,8 @@
         }
         $ids = filter_input(INPUT_POST, 'ids');
         $name = filter_input(INPUT_POST, 'name');
+        $leader_id = filter_input(INPUT_POST, 'leader');
+        $total_front = filter_input(INPUT_POST, 'front');
         $description = filter_input(INPUT_POST, 'description');
         $id = preg_split("/[\s,]+/", $ids);
         $id_count = sizeof($id);
@@ -171,15 +173,24 @@
         $statement->bindValue(':area', $area);
         $statement->bindValue(':stage', $stage);
         $statement->bindValue(':difficulty', $difficulty);
-        error_log($statement);
         $res = $statement->execute();
         if (!$res){
             return -5;
         }
+
+        $j = 0;
         foreach ($id as $i){
-            // TODO: Leader and front
-            $s = "INSERT INTO team_unit VALUES ($team_id, $i, 0, 0);";
+            $leader = 0;
+            if ((string) $i == (string) $leader_id){
+                $leader = 1;
+            }
+            $front = 0;
+            if ($j < $total_front){
+                $front = 1;
+            }
+            $s = "INSERT INTO team_unit VALUES ($team_id, $i, $leader, $front);";
             $db->query($s);
+            $j ++;
         }
         return 0;
     }

+ 1 - 1
application/config.php

@@ -49,7 +49,7 @@
         /**
          * CSS full url (includes final '/').
          */
-        const CSS = BASE_URL . "/css/";
+        const CSS = BASE_URL . "css/";
 
         /**
          * URLs to image resources.

+ 2 - 3
application/entity/K_Inventory.php

@@ -17,7 +17,7 @@
      *
      * Represents an object from the table 'k_inventory'.
      *
-     * @category Entity 
+     * @category Entity
      */
     class K_Inventory extends Entity{
 
@@ -35,7 +35,7 @@
          * @var string Item type name.
          */
         public $type;
-        
+
         /**
          * @var string Item description.
          */
@@ -72,7 +72,6 @@
             $q = $db->query($s);
             $r = $q->fetchArray(SQLITE3_ASSOC);
             $this->type = $r["type"];
-            $this->amount = $r["amount"];
             $this->id = $r["id"];
             $this->name = HTML::e($r["name"]);
             $this->description = HTML::e($r["description"]);

+ 2 - 2
application/entity/K_Leader_Skill.php

@@ -114,10 +114,10 @@
                 case STAT_ID::CRIT_DMG:
                     $s .= "critical damage";
                     break;
-                case STAT_ID::ACC:
+                case STAT_ID::ACCURACY:
                     $s .= "accuracy";
                     break;
-                case STAT_ID::RES:
+                case STAT_ID::RESIST:
                     $s .= "resistance";
                     break;
             }

+ 33 - 25
application/entity/Rune_Craft.php

@@ -96,6 +96,11 @@
          */
         public $value;
 
+        /**
+         * @var int Number of item of this type owned.
+         */
+        public $amount;
+
         /**
          * Constructor.
          *
@@ -109,31 +114,33 @@
 
             global $db;
 
-            $s =
-              "SELECT " .
-              "  rune_craft.id AS id, " .
-              "  rune_craft.quality As quality, " .
-              "  rune_craft.rune AS rune, " .
-              "  rune_craft.stat AS stat, " .
-              "  rune_craft.value AS value, " .
-              "  k_rune_craft_type.name AS name, " .
-              "  k_rune_craft_type.gem AS gem, " .
-              "  k_rune_craft_type.inmemorial AS inmemorial, " .
-              "  k_rune_craft_type.ancient AS ancient, " .
-              "  k_rune_craft_range.min AS min, " .
-              "  k_rune_craft_range.max AS max " .
-              "FROM " .
-              "  rune_craft, ".
-              "  k_rune_craft_type, " .
-              "  k_rune_craft_range " .
-              "WHERE " . 
-              "  rune_craft.id = $id AND " .
-              "  rune_craft.type = k_rune_craft_type.id AND " .
-              "  k_rune_craft_type.gem = k_rune_craft_range.gem AND " .
-              "  k_rune_craft_type.gem = k_rune_craft_range.gem AND " .
-              "  k_rune_craft_type.ancient = k_rune_craft_range.ancient AND " .
-              "  rune_craft.quality = k_rune_craft_range.quality AND " .
-              "  rune_craft.stat = k_rune_craft_range.stat;";
+            $s = "
+              SELECT
+                rune_craft.id AS id,
+                rune_craft.quality As quality,
+                rune_craft.rune AS rune,
+                rune_craft.stat AS stat,
+                rune_craft.value AS value,
+                rune_craft.amount AS amount,
+                k_rune_craft_type.name AS name,
+                k_rune_craft_type.gem AS gem,
+                k_rune_craft_type.inmemorial AS inmemorial,
+                k_rune_craft_type.ancient AS ancient,
+                k_rune_craft_range.min AS min,
+                k_rune_craft_range.max AS max
+              FROM
+                rune_craft,
+                k_rune_craft_type,
+                k_rune_craft_range
+              WHERE
+                rune_craft.id = $id AND
+                rune_craft.type = k_rune_craft_type.id AND
+                k_rune_craft_type.gem = k_rune_craft_range.gem AND
+                k_rune_craft_type.gem = k_rune_craft_range.gem AND
+                k_rune_craft_type.ancient = k_rune_craft_range.ancient AND
+                rune_craft.quality = k_rune_craft_range.quality AND
+                rune_craft.stat = k_rune_craft_range.stat;
+            ";
             $q = $db->query($s);
             $r = $q->fetchArray(SQLITE3_ASSOC);
             if ($r){
@@ -155,6 +162,7 @@
                 $this->min = $r["min"];
                 $this->max = $r["max"];
                 $this->value = $r["value"];
+                $this->amount = $r["amount"];
             }
         }
 

+ 3 - 0
application/helper/HTML_Helper.php

@@ -392,6 +392,9 @@
             $html .= "<span class='craft_stat_name'>" . $stat_name . "</span>\n";
             $html .= "<span class='craft_stat_value'>" . $stat_value . "</span>\n";
             $html .= "</span>\n";
+            $html .= "<span class='craft_amount'>\n";
+            $html .= "x" . $craft->amount . "\n";
+            $html .= "</span>\n";
             $html .= "</div>\n";
             return $html;
         }

+ 3 - 3
application/page/Fusion_Page.php

@@ -70,7 +70,7 @@
         private function parse_filters(){
             $this->filters = FILTER::FUSION;
             if (isset($_GET["product"]) && intval($_GET["product"]) > 0){
-                $this->filters["product"] = $_GET["product"];
+                $this->filters["PRODUCT"] = $_GET["product"];
             }
             return;
         }
@@ -86,8 +86,8 @@
               "SELECT DISTINCT product " .
               "FROM k_fusion " .
               "WHERE stars = 5 ";
-            if ($this->filters["product"] > 0){
-                $s = $s . " AND product = " . $this->filters["product"] . " ";
+            if ($this->filters["PRODUCT"] > 0){
+                $s = $s . " AND product = " . $this->filters["PRODUCT"] . " ";
             }
             $s = $s . "ORDER BY stars DESC;";
             return $s;

+ 10 - 3
application/page/Home_Page.php

@@ -193,9 +193,16 @@
                 array_push($this->defense, new Unit($r["unit"], false));
             }
             $s = "
-                SELECT DISTINCT id
-                FROM building
-                WHERE uid = '$UID';
+                SELECT
+                  building.id,
+                  min(building)
+                FROM
+                  building,
+                  k_building
+                WHERE
+                  uid = '$UID' AND
+                  building.building = k_building.id 
+                GROUP BY building.building;
             ";
             $q = $db->query($s);
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){

+ 2 - 2
application/page/Monster_Page.php

@@ -35,11 +35,11 @@
             "spd" => 0,
             "crd" => 0,
             "elm" => [
-                0 => 0,
                 1 => 0,
                 2 => 0,
                 3 => 0,
-                4 => 0
+                4 => 0,
+                5 => 0
             ]
         ];
 

+ 2 - 2
application/page/Report_Skillup_Page.php

@@ -77,7 +77,7 @@
                 $this->filters["MAXED"] = true;
             }
             if (isset($_GET["2a"]) && $_GET["2a"] == "on"){
-                $this->filters["2a"] = true;
+                $this->filters["2A"] = true;
             }
             if (isset($_GET["homunculus"]) && $_GET["homunculus"] == "on"){
                 $this->filters["HOMUNCULUS"] = true;
@@ -119,7 +119,7 @@
                 unit.stars >= " . $this->filters["MIN_STARS"] . " AND
                 unit.stars <= " . $this->filters["MAX_STARS"]
             ;
-            if ($this->filters["2a"]){
+            if ($this->filters["2A"]){
                 $s = $s . "
                     AND ((
                       k_unit.natural_stars >= " . $this->filters["MIN_NATURAL_STARS"] . " AND

+ 0 - 1
application/page/Runes_Page.php

@@ -56,7 +56,6 @@
             else{
                 $s = $this->build_custom_query($_GET["custom_filter"]);
             }
-            error_log($s);
             $q = $db->query($s);
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 $rune = new Rune($r["id"]);

+ 3 - 0
application/page/Runs_Page.php

@@ -102,6 +102,9 @@
                 FROM run
                 WHERE uid = '$UID'
              ";
+            if ($this->filters["AREA_TYPE"] > 0){
+                $s = $s . " AND area_type =  " . $this->filters["AREA_TYPE"] . " ";
+            }
             if ($this->filters["AREA"] > 0){
                 $s = $s . " AND area =  " . $this->filters["AREA"] . " ";
             }

+ 256 - 0
application/page/Stats_Page.php

@@ -0,0 +1,256 @@
+ <?php
+    /**
+     * Statistics 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 . "Run.php");
+
+    /**
+     * Statistics page model.
+     *
+     * @category Page
+     */
+    class Stats_Page extends Page{
+
+        /**
+         * @var bool Indicates if there is data to show.
+         */
+        public $show_data = true;
+
+        /**
+         * @var Run The best run for the selected area
+         */
+        public $best_run = null;
+
+        /**
+         * @var int[] 0 index: total victories. 1 index: total losses.
+         */
+        public $win_lose = [0, 0];
+
+        /**
+         * @var int[] Item type drops.
+         */
+        public $drop_type = [
+            "rune" => 0,
+            "rune_craft" => 0,
+            "item" => 0,
+            "sd" => 0,
+            "shapeshifting" => 0,
+            "unit" => 0
+        ];
+
+        /**
+         * @var int[] Rune drop rate by slot.
+         */
+        public $drop_rune_slot = [
+            "1" => 0,
+            "2" => 0,
+            "3" => 0,
+            "4" => 0,
+            "5" => 0,
+            "6" => 0
+        ];
+
+        /**
+         * @var int[] Rune drop rate by stars.
+         */
+        public $drop_rune_stars = [
+            "1" => 0,
+            "2" => 0,
+            "3" => 0,
+            "4" => 0,
+            "5" => 0,
+            "6" => 0
+        ];
+
+        public $drop_rune_quality = [
+            QUALITY_ID::NORMAL => 0,
+            QUALITY_ID::MAGIC => 0,
+            QUALITY_ID::RARE => 0,
+            QUALITY_ID::HERO => 0,
+            QUALITY_ID::LEGEND => 0
+        ];
+
+        /**
+         * Constructor.
+         *
+         * Retrieves the data and initializes the variables.
+         *
+         * @global int Player ID.
+         * @global resource Database connection.
+         */
+        public function __construct(){
+            global $UID;
+            global $db;
+            $best_run = null;
+            $this->view = PATH::VIEW . "stats.php";
+            $this->title = "Stats - SWDB";
+            $this->description = "Player statistics";
+            $this->canonical = URL::BASE . "stats/";
+
+            $this->parse_filters();
+
+            // If no filters, stop
+            if ($this->filters["AREA"] == null){
+                $this->show_data = false;
+                return;
+            }
+
+            // TODO DEBUG Force NB10
+            /*$this->filters["AREA"] = 6001;
+            $this->filters["AREA_TYPE"] = 2;
+            $this->filters["STAGE"] = 10;*/
+            // TODO END DEBUG
+
+            // Build query modifier:
+            $query_where = "
+              run.uid = '$UID' AND
+              run.area = " . $this->filters["AREA"] . "
+            ";
+            if ($this->filters["STAGE"] > 0){
+                $query_where = $query_where . " AND run.stage =  " . $this->filters["STAGE"] . " ";
+            }
+            if ($this->filters["DIFFICULTY"] > 0){
+                $query_where = $query_where . " AND run.difficulty =  " . $this->filters["DIFFICULTY"] . " ";
+            }
+            if (!$this->filters["INCLUDE_HELPER"]){
+                $query_where = $query_where . " AND run.helper = 0 ";
+            }
+
+            // Best run:
+            $s = "
+              SELECT id
+              FROM run
+              WHERE
+                $query_where
+              ORDER BY time DESC
+              LIMIT 1;
+            ";
+            $q = $db->query($s);
+            if ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                $this->best_run = new Run($r["id"]);
+            }
+
+            // Win/lose ratio
+            $s = "
+              SELECT
+                (SELECT COUNT(win) FROM run WHERE $query_where AND win = 1 ) AS win,
+                (SELECT COUNT(win) FROM run WHERE $query_where AND win = 0) AS lose;
+            ";
+            $q = $db->query($s);
+            $r = $q->fetchArray(SQLITE3_ASSOC);
+            $this->win_lose = [$r["win"], $r["lose"]];
+
+            // Drop type rate
+            $s = "
+              SELECT
+                (SELECT COUNT(run) FROM run, run_drop_rune WHERE $query_where AND run.id = run_drop_rune.run AND uid = '$UID') AS rune,
+                (SELECT COUNT(run) FROM run, run_drop_rune_craft WHERE $query_where AND run.id = run_drop_rune_craft.run AND uid = '$UID') AS rune_craft,
+                (SELECT COUNT(run) FROM run, run_drop_item WHERE $query_where AND run.id = run_drop_item.run AND uid = '$UID') AS item,
+                (SELECT COUNT(run) FROM run, run_drop_sd WHERE $query_where AND run.id = run_drop_sd.run AND uid = '$UID') AS sd,
+                (SELECT COUNT(run) FROM run, run_drop_shapeshifting WHERE $query_where AND run.id = run_drop_shapeshifting.run AND uid = '$UID') AS shapeshifting,
+                (SELECT COUNT(run) FROM run, run_drop_unit WHERE $query_where AND run.id = run_drop_unit.run AND uid = '$UID') AS unit;
+            ";
+            $q = $db->query($s);
+            $r = $q->fetchArray(SQLITE3_ASSOC);
+            $this->drop_type["rune"] = $r["rune"];
+            $this->drop_type["rune_craft"] = $r["rune_craft"];
+            $this->drop_type["item"] = $r["item"];
+            $this->drop_type["sd"] = $r["sd"];
+            $this->drop_type["shapeshifting"] = $r["shapeshifting"];
+            $this->drop_type["unit"] = $r["unit"];
+
+            // Rune drop rate by slot
+            $s = "
+              SELECT 
+                slot,
+                count(run.id) AS count
+              FROM
+                run,
+                run_drop_rune
+              WHERE
+                $query_where AND 
+                run.id = run_drop_rune.run
+              GROUP BY slot
+              ORDER BY slot;
+            ";
+            $q = $db->query($s);
+            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                $this->drop_rune_slot[$r["slot"]] = $r["count"];
+            }
+
+            // Rune drop rate by slot
+            $s = "
+              SELECT 
+                stars,
+                count(run.id) AS count
+              FROM
+                run,
+                run_drop_rune
+              WHERE
+                $query_where AND 
+                run.id = run_drop_rune.run
+              GROUP BY stars
+              ORDER BY stars DESC;
+            ";
+            $q = $db->query($s);
+            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                $this->drop_rune_stars[strval($r["stars"])] = $r["count"];
+            }
+
+            // Rune drop rate by quality
+            $s = "
+              SELECT 
+                quality,
+                count(run.id) AS count
+              FROM
+                run,
+                run_drop_rune
+              WHERE
+                $query_where AND 
+                run.id = run_drop_rune.run
+              GROUP BY quality
+              ORDER BY quality DESC;
+            ";
+            $q = $db->query($s);
+            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                $this->drop_rune_quality[strval($r["quality"])] = $r["count"];
+            }
+        }
+
+        /**
+         * Parses the request looking for the selected filters, validates them
+         * and adds them to the $filters array.
+         */
+        private function parse_filters(){
+            $this->filters = FILTER::STATS;
+            if (isset($_GET["area_type"]) && intval($_GET["area_type"]) > 0){
+                $this->filters["AREA_TYPE"] = $_GET["area_type"];
+            }
+            if (isset($_GET["area"]) && intval($_GET["area"]) > 0){
+                $this->filters["AREA"] = $_GET["area"];
+            }
+            if (isset($_GET["stage"]) && intval($_GET["stage"]) > 0){
+                $this->filters["STAGE"] = $_GET["stage"];
+            }
+            if (isset($_GET["difficulty"]) && intval($_GET["difficulty"]) >= DIFFICULTY_ID::NORMAL && intval($_GET["difficulty"]) <= DIFFICULTY_ID::HELL){
+                $this->filters["DIFFICULTY"] = $_GET["difficulty"];
+            }
+            if (isset($_GET["include_helper"]) && $_GET["include_helper"] == "on"){
+                $this->filters["INCLUDE_HELPER"] = true;
+            }
+            else{
+                $this->filters["INCLUDE_HELPER"] = false;
+            }
+            return;
+        }
+    }
+?>

+ 2 - 3
application/view/fusion.php

@@ -69,7 +69,6 @@
             <article id='filters_content'>
 <?php
                 $filters = $page->filters;
-                $custom_filters = $page->custom_filters;
                 include __DIR__ . "/inc/filter_fusion.php";
 ?>
             </article>
@@ -279,7 +278,7 @@
 <?php
                                                         foreach ($sub_fusion->ingredient_unawaken[$num]->essences as $essence){
 ?>
-                                                            <div class='essence border_<?=$essence["element"]?>'>
+                                                            <div class='essence border_<?=explode(" ", $essence["item"]->name)[2]?>'>
                                                                 <img alt='<?=$essence["item"]->name?>' title='<?=$essence["amount"]?>x <?=$essence["item"]->name?>' class='essence' src='<?=$essence["item"]->get_image()?>'/>
                                                                 <span>
                                                                     <?=$essence["amount"]?>
@@ -390,7 +389,7 @@
 <?php
                                         foreach ($fusion->ingredient_unawaken[0]->essences as $essence){
 ?>
-                                            <div class='essence border_<?=$essence["element"]?>'>
+                                            <div class='essence border_<?=explode(" ", $essence["item"]->name)[2]?>'>
                                                 <img alt='<?=$essence["item"]->name?>' title='<?=$essence["amount"]?>x <?=$essence["item"]->name?>' class='essence' src='<?=$essence["item"]->get_image()?>'/>
                                                 <span>
                                                     <?=$essence["amount"]?>

+ 3 - 3
application/view/inc/filter_report_runeupgrade.php

@@ -36,11 +36,11 @@
         </script>
         <form action='/<?=$UID?>/report/runeupgrade/' method='get' id='custom_filter_runes' class='custom_filter'>
             <span class='custom_filter_title'>Custom filters:</span>
-            <select name='custom_filter' onChange='customFilterDescription(this.value);' onload='customFilterDescription("<?=$_GET["custom_filter"]?>");'>
+            <select name='custom_filter' onChange='customFilterDescription(this.value);' onload='customFilterDescription("<?=filter_input(INPUT_GET, 'custom_filter')?>");'>
                 <option> </option>
 <?php
                  foreach($custom_filters AS $filter){
-                    if ($_GET["custom_filter"] == $filter->id){
+                    if (filter_input(INPUT_GET, 'custom_filter') == $filter->id){
                         $selected = "selected='selected'";
                     }
                     else{
@@ -55,7 +55,7 @@
             <input type='submit' value='Apply'/>
             <p id='custom_filter_description'> </p>
             <script>
-                customFilterDescription("<?=$_GET["custom_filter"]?>");
+                customFilterDescription("<?=filter_input(INPUT_GET, 'custom_filter')?>");
             </script>
         </form>
 <?php

+ 8 - 8
application/view/inc/filter_runes.php

@@ -34,11 +34,11 @@
         </script>
         <form action='/<?=$UID?>/runes/' method='get' id='custom_filter_runes' class='custom_filter'>
             <span class='custom_filter_title'>Custom filters:</span>
-            <select name='custom_filter' onChange='customFilterDescription(this.value);' onload='alert("LOAD");customFilterDescription("<?=$_GET["custom_filter"]?>");'>
+            <select name='custom_filter' onChange='customFilterDescription(this.value);' onload='alert("LOAD");customFilterDescription("<?=filter_input(INPUT_GET, 'custom_filter')?>");'>
                 <option> </option>
 <?php
                  foreach($custom_filters AS $filter){
-                    if ($_GET["custom_filter"] == $filter->id){
+                    if (filter_input(INPUT_GET, 'custom_filter') == $filter->id){
                         $selected = "selected='selected'";
                     }
                     else{
@@ -53,7 +53,7 @@
             <input type='submit' value='Apply'/>
             <p id='custom_filter_description'> </p>
             <script>
-                customFilterDescription("<?=$_GET["custom_filter"]?>");
+                customFilterDescription("<?=filter_input(INPUT_GET, 'custom_filter')?>");
             </script>
         </form>
 <?php
@@ -68,7 +68,7 @@
                 <select multiple name='type[]' id='filter_type'>
 <?php
 
-                    $reflect = new ReflectionClass(RUNE_SET_ID);
+                    $reflect = new ReflectionClass("RUNE_SET_ID");
                     foreach($reflect->getConstants() as $name => $id){
                         $selected = "";
                         if (in_array($id, $filters["TYPE"])){
@@ -120,7 +120,7 @@
                 <span>Main stat:</span>
                 <select multiple name='main_stat[]' id='filter_main_stat'>
 <?php
-                    $reflect = new ReflectionClass(RUNE_STAT_ID);
+                    $reflect = new ReflectionClass("RUNE_STAT_ID");
                     foreach($reflect->getConstants() as $name => $id){
                         $selected = "";
                         if (in_array($id, $filters["MAIN"])){
@@ -128,7 +128,7 @@
                         }
                         $name_format = ucwords(str_replace("_P", "%", $name));
 ?>
-                        <option value='<?=$n?>' <?=$selected?>><?=$name_format?></option>
+                        <option value='<?=$id?>' <?=$selected?>><?=$name_format?></option>
 <?php
                     }
 ?>
@@ -138,7 +138,7 @@
                 <span>Has sub stat:</span>
                 <select multiple name='sub_stat[]' id='filter_sub_stat'>
 <?php
-                    $reflect = new ReflectionClass(RUNE_STAT_ID);
+                    $reflect = new ReflectionClass("RUNE_STAT_ID");
                     foreach($reflect->getConstants() as $name => $id){
                         $selected = "";
                         if (in_array($id, $filters["SUB"])){
@@ -146,7 +146,7 @@
                         }
                         $name_format = ucwords(str_replace("_P", "%", $name));
 ?>
-                        <option value='<?=$n?>' <?=$selected?>><?=$name_format?></option>
+                        <option value='<?=$id?>' <?=$selected?>><?=$name_format?></option>
 <?php
                     }
 ?>

+ 34 - 30
application/view/inc/filter_runs.php

@@ -17,13 +17,12 @@
                     Area
                 </span>
             </td>
-            <td class='filter'>
+            <td class='filter' colspan='2'>
 <?php
                 $selected = array_fill(0, 12 + 1, "");
                 $selected[$filters["AREA_TYPE"]] = "selected";
 ?>
-                <span>Region:</span>
-                <select name='area_type' id='filter_area'>
+                <select name='area_type' id='filter_area_type' onchange='selectAreaType();'>
                     <option value='0'>All</option>
                     <option value='<?=AREA_TYPE_ID::SCENARIO?>' <?=$selected[AREA_TYPE_ID::SCENARIO]?>>Scenario</option>
                     <option value='<?=AREA_TYPE_ID::CAIROS_DUNGEON?>' <?=$selected[AREA_TYPE_ID::CAIROS_DUNGEON]?>>Cairos Dungeon</option>
@@ -38,40 +37,45 @@
                     <option value='<?=AREA_TYPE_ID::WORLD_BOSS?>' <?=$selected[AREA_TYPE_ID::WORLD_BOSS]?>>World Boss</option>
                     <option value='<?=AREA_TYPE_ID::DIMENSIONAL_RIFT?>' <?=$selected[AREA_TYPE_ID::DIMENSIONAL_RIFT]?>>Dimensional Rift</option>
                 </select>
-            </td>
-            <td class='filter'>
 <?php
                 $selected = array_fill(0, 9002, "");
                 $selected[$filters["AREA"]] = "selected";
 ?>
-                <span>Area:</span>
-                <select name='area_scenario'>
+                <select id='filter_area_scenario' class='filter_area'>
+                    <option value='0'>All</option>
+                    <option value='<?=SCENARIO_ID::GAREN_FOREST?>' <?=$selected[SCENARIO_ID::GAREN_FOREST]?>>Garen Forest</option>
+                    <option value='<?=SCENARIO_ID::MT_SIZ?>' <?=$selected[SCENARIO_ID::MT_SIZ]?>>Mt. Siz</option>
+                    <option value='<?=SCENARIO_ID::KABIR_RUINS?>' <?=$selected[SCENARIO_ID::KABIR_RUINS]?>>Kabir Ruins</option>
+                    <option value='<?=SCENARIO_ID::MT_WHITE_RAGON?>' <?=$selected[SCENARIO_ID::MT_WHITE_RAGON]?>>Mt. White Ragon</option>
+                    <option value='<?=SCENARIO_ID::TELAIN_FOREST?>' <?=$selected[SCENARIO_ID::TELAIN_FOREST]?>>Telain Forest</option>
+                    <option value='<?=SCENARIO_ID::HYDENI_RUINS?>' <?=$selected[SCENARIO_ID::HYDENI_RUINS]?>>Hydeni Ruins</option>
+                    <option value='<?=SCENARIO_ID::TAMOR_DESERT?>' <?=$selected[SCENARIO_ID::TAMOR_DESERT]?>>Tamor Desert</option>
+                    <option value='<?=SCENARIO_ID::VROFAGUS_RUINS?>' <?=$selected[SCENARIO_ID::VROFAGUS_RUINS]?>>Vrofagus Ruins</option>
+                    <option value='<?=SCENARIO_ID::FAIMON_VOLCANO?>' <?=$selected[SCENARIO_ID::FAIMON_VOLCANO]?>>Faimon Volcano</option>
+                    <option value='<?=SCENARIO_ID::AIDEN_FOREST?>' <?=$selected[SCENARIO_ID::AIDEN_FOREST]?>>Aiden Forest</option>
+                    <option value='<?=SCENARIO_ID::FERUN_CASTLE?>' <?=$selected[SCENARIO_ID::FERUN_CASTLE]?>>Ferun Castle</option>
+                    <option value='<?=SCENARIO_ID::MT_RUNAR?>' <?=$selected[SCENARIO_ID::MT_RUNAR]?>>Mt. Runar</option>
+                    <option value='<?=SCENARIO_ID::CHIRUKA_REMAINS?>' <?=$selected[SCENARIO_ID::CHIRUKA_REMAINS]?>>Charuka Remains</option>
+                </select>
+                <select id='filter_area_cairos' class='filter_area'>
                     <option value='0'>All</option>
-                    <option value='1' <?=$selected[1]?>>Garen Forest</option>
-                    <option value='2' <?=$selected[2]?>>Mt. Siz</option>
-                    <option value='3' <?=$selected[3]?>>Kabir Ruins</option>
-                    <option value='4' <?=$selected[4]?>>Mt. White Ragon</option>
-                    <option value='5' <?=$selected[5]?>>Telain Forest</option>
-                    <option value='6' <?=$selected[6]?>>Hydeni Ruins</option>
-                    <option value='7' <?=$selected[7]?>>Tamor Desert</option>
-                    <option value='8' <?=$selected[8]?>>Vrofagus Ruins</option>
-                    <option value='9' <?=$selected[9]?>>Faimon Volcano</option>
-                    <option value='10' <?=$selected[10]?>>Aiden Forest</option>
-                    <option value='11' <?=$selected[11]?>>Ferun Castle</option>
-                    <option value='12' <?=$selected[12]?>>Mt. Runar</option>
-                    <option value='13' <?=$selected[13]?>>Charuka Remains</option>
+                    <option value='<?=DUNGEON_ID::GIANTS_KEEP?>' <?=$selected[DUNGEON_ID::GIANTS_KEEP]?>>Giant&#39;s Keep</option>
+                    <option value='<?=DUNGEON_ID::DRAGONS_LAIR?>' <?=$selected[DUNGEON_ID::DRAGONS_LAIR]?>>Dragon&#39;s Lair</option>
+                    <option value='<?=DUNGEON_ID::NECROPOLIS?>' <?=$selected[DUNGEON_ID::NECROPOLIS]?>>Necropolis</option>
+                    <option value='<?=DUNGEON_ID::HALL_OF_MAGIC?>' <?=$selected[DUNGEON_ID::HALL_OF_MAGIC]?>>Hall of Magic</option>
+                    <option value='<?=DUNGEON_ID::HALL_OF_LIGHT?>' <?=$selected[DUNGEON_ID::HALL_OF_LIGHT]?>>Hall of Light</option>
+                    <option value='<?=DUNGEON_ID::HALL_OF_DARK?>' <?=$selected[DUNGEON_ID::HALL_OF_DARK]?>>Hall of Dark</option>
+                    <option value='<?=DUNGEON_ID::HALL_OF_FIRE?>' <?=$selected[DUNGEON_ID::HALL_OF_FIRE]?>>Hall of Fire</option>
+                    <option value='<?=DUNGEON_ID::HALL_OF_WATER?>' <?=$selected[DUNGEON_ID::HALL_OF_WATER]?>>Hall of Water</option>
+                    <option value='<?=DUNGEON_ID::HALL_OF_WIND?>' <?=$selected[DUNGEON_ID::HALL_OF_WIND]?>>Hall of Wind</option>
                 </select>
-                <select name='area_cairos'>
+                <select id='filter_area_rift_dungeon' class='filter_area'>
                     <option value='0'>All</option>
-                    <option value='1001' <?=$selected[1001]?>>Hall of Dark</option>
-                    <option value='2001' <?=$selected[2001]?>>Hall of Fire</option>
-                    <option value='3001' <?=$selected[3001]?>>Hall of Water</option>
-                    <option value='4001' <?=$selected[4001]?>>Hall of Wind</option>
-                    <option value='5001' <?=$selected[5001]?>>Hall of Magic</option>
-                    <option value='6001' <?=$selected[6001]?>>Necropolis</option>
-                    <option value='7001' <?=$selected[7001]?>>Hall of Light</option>
-                    <option value='8001' <?=$selected[8001]?>>Giant&#39;s Keep</option>
-                    <option value='9001' <?=$selected[9001]?>>Dragon&#39;s Lair</option>
+                    <option value='<?=ELEMENTAL_RIFT_DUNGEON_ID::LIGHT_BEAST?>' <?=$selected[ELEMENTAL_RIFT_DUNGEON_ID::LIGHT_BEAST]?>>Light Beast</option>
+                    <option value='<?=ELEMENTAL_RIFT_DUNGEON_ID::DARK_BEAST?>' <?=$selected[ELEMENTAL_RIFT_DUNGEON_ID::DARK_BEAST]?>>Dark Beast</option>
+                    <option value='<?=ELEMENTAL_RIFT_DUNGEON_ID::FIRE_BEAST?>' <?=$selected[ELEMENTAL_RIFT_DUNGEON_ID::FIRE_BEAST]?>>Fire Beast</option>
+                    <option value='<?=ELEMENTAL_RIFT_DUNGEON_ID::ICE_BEAST?>' <?=$selected[ELEMENTAL_RIFT_DUNGEON_ID::ICE_BEAST]?>>Ice Beast</option>
+                    <option value='<?=ELEMENTAL_RIFT_DUNGEON_ID::WIND_BEAST?>' <?=$selected[ELEMENTAL_RIFT_DUNGEON_ID::WIND_BEAST]?>>Wind Beast</option>
                 </select>
             </td>
         </tr>

+ 82 - 0
application/view/inc/filter_stats.php

@@ -0,0 +1,82 @@
+<?php
+    /**
+     * Stat  filter view.
+     *
+     * To be included from the stats view.
+     *
+     * @category View
+     * @property_read mixed[] $filters Selected filters.
+     * @property_read int $page Player ID.
+     */
+?>
+<form action='/<?=$UID?>/stats/' method='get' id='filter_runeupgrade' class='filter'>
+    <table>
+        <tr>
+            <td class='filter'>
+                <span>Area:</span>
+            </td>
+        </tr>
+        <tr>
+            <td class='filter'>
+<?php
+                $selected = array_fill(0, 12 + 1, "");
+                $selected[$filters["AREA_TYPE"]] = "selected";
+?>
+                <select name='area_type' id='filter_area_type' onChange='selectAreaType();'>
+                    <option value='0'></option>
+                    <option value='<?=AREA_TYPE_ID::SCENARIO?>' <?=$selected[AREA_TYPE_ID::SCENARIO]?>>Scenario</option>
+                    <option value='<?=AREA_TYPE_ID::CAIROS_DUNGEON?>' <?=$selected[AREA_TYPE_ID::CAIROS_DUNGEON]?>>Cairos Dungeon</option>
+                    <option value='<?=AREA_TYPE_ID::RIFT_DUNGEON?>' <?=$selected[AREA_TYPE_ID::RIFT_DUNGEON]?>>Rift Beast</option>
+                    <option value='<?=AREA_TYPE_ID::RIFT_RAID?>' <?=$selected[AREA_TYPE_ID::RIFT_RAID]?>>Rift Raid</option>
+                    <option value='<?=AREA_TYPE_ID::DIMENSIONAL_HOLE?>' <?=$selected[AREA_TYPE_ID::DIMENSIONAL_HOLE]?>>Dimensional Hole</option>
+                </select>
+<?php
+                $selected = array_fill(0, 9002, "");
+                $selected[$filters["AREA"]] = "selected";
+?>
+                <select class='filter_area' id='filter_area_scenario'>
+                    <option value='0'></option>
+                    <option value='<?=AREA_TYPE_ID::SCENARIO?>' <?=$selected[AREA_TYPE_ID::SCENARIO]?>>Scenario</option>
+                    <option value='<?=AREA_TYPE_ID::CAIROS_DUNGEON?>' <?=$selected[AREA_TYPE_ID::CAIROS_DUNGEON]?>>Cairos Dungeon</option>
+                    <option value='<?=AREA_TYPE_ID::RIFT_DUNGEON?>' <?=$selected[AREA_TYPE_ID::RIFT_DUNGEON]?>>Rift Beast</option>
+                    <option value='<?=AREA_TYPE_ID::RIFT_RAID?>' <?=$selected[AREA_TYPE_ID::RIFT_RAID]?>>Rift Raid</option>
+                    <option value='<?=AREA_TYPE_ID::DIMENSIONAL_HOLE?>' <?=$selected[AREA_TYPE_ID::DIMENSIONAL_HOLE]?>>Dimensional Hole</option>
+                    <option value='<?=AREA_TYPE_ID::ARENA?>' <?=$selected[AREA_TYPE_ID::ARENA]?>>Arena</option>
+                    <option value='<?=AREA_TYPE_ID::GUILD_WAR?>' <?=$selected[AREA_TYPE_ID::GUILD_WAR]?>>Guild War</option>
+                    <option value='<?=AREA_TYPE_ID::GUILD_SIEGE?>' <?=$selected[AREA_TYPE_ID::GUILD_SIEGE]?>>Guild Siege</option>
+                    <option value='<?=AREA_TYPE_ID::TARTARUS_LABYRINTH?>' <?=$selected[AREA_TYPE_ID::TARTARUS_LABYRINTH]?>>Tartarus Labyrinth</option>
+                    <option value='<?=AREA_TYPE_ID::TRIAL_OF_ASCENSION?>' <?=$selected[AREA_TYPE_ID::TRIAL_OF_ASCENSION]?>>Trial of Ascension</option>
+                    <option value='<?=AREA_TYPE_ID::WORLD_BOSS?>' <?=$selected[AREA_TYPE_ID::WORLD_BOSS]?>>World Boss</option>
+                    <option value='<?=AREA_TYPE_ID::DIMENSIONAL_RIFT?>' <?=$selected[AREA_TYPE_ID::DIMENSIONAL_RIFT]?>>Dimensional Rift</option>
+                </select>
+                <select class='filter_area' id='filter_area_cairos'>
+                    <option value='0'></option>
+                    <option value='<?=DUNGEON_ID::GIANTS_KEEP?>' <?=$selected[DUNGEON_ID::GIANTS_KEEP]?>>Giant&#39;s Keep</option>
+                    <option value='<?=DUNGEON_ID::DRAGONS_LAIR?>' <?=$selected[DUNGEON_ID::DRAGONS_LAIR]?>>Dragon&#39;s Lair</option>
+                    <option value='<?=DUNGEON_ID::NECROPOLIS?>' <?=$selected[DUNGEON_ID::NECROPOLIS]?>>Necropolis</option>
+                    <option value='<?=DUNGEON_ID::HALL_OF_MAGIC?>' <?=$selected[DUNGEON_ID::HALL_OF_MAGIC]?>>Hall of Magic</option>
+                    <option value='<?=DUNGEON_ID::HALL_OF_LIGHT?>' <?=$selected[DUNGEON_ID::HALL_OF_LIGHT]?>>Hall of Light</option>
+                    <option value='<?=DUNGEON_ID::HALL_OF_DARK?>' <?=$selected[DUNGEON_ID::HALL_OF_DARK]?>>Hall of Dark</option>
+                    <option value='<?=DUNGEON_ID::HALL_OF_FIRE?>' <?=$selected[DUNGEON_ID::HALL_OF_FIRE]?>>Hall of Fire</option>
+                    <option value='<?=DUNGEON_ID::HALL_OF_WATER?>' <?=$selected[DUNGEON_ID::HALL_OF_WATER]?>>Hall of Water</option>
+                    <option value='<?=DUNGEON_ID::HALL_OF_WIND?>' <?=$selected[DUNGEON_ID::HALL_OF_WIND]?>>Hall of Wind</option>
+                </select>
+                <select id='filter_area_rift_dungeon' class='filter_area'>
+                    <option value='0'></option>
+                    <option value='<?=ELEMENTAL_RIFT_DUNGEON_ID::LIGHT_BEAST?>' <?=$selected[ELEMENTAL_RIFT_DUNGEON_ID::LIGHT_BEAST]?>>Light Beast</option>
+                    <option value='<?=ELEMENTAL_RIFT_DUNGEON_ID::DARK_BEAST?>' <?=$selected[ELEMENTAL_RIFT_DUNGEON_ID::DARK_BEAST]?>>Dark Beast</option>
+                    <option value='<?=ELEMENTAL_RIFT_DUNGEON_ID::FIRE_BEAST?>' <?=$selected[ELEMENTAL_RIFT_DUNGEON_ID::FIRE_BEAST]?>>Fire Beast</option>
+                    <option value='<?=ELEMENTAL_RIFT_DUNGEON_ID::ICE_BEAST?>' <?=$selected[ELEMENTAL_RIFT_DUNGEON_ID::ICE_BEAST]?>>Ice Beast</option>
+                    <option value='<?=ELEMENTAL_RIFT_DUNGEON_ID::WIND_BEAST?>' <?=$selected[ELEMENTAL_RIFT_DUNGEON_ID::WIND_BEAST]?>>Wind Beast</option>
+                </select>
+                <select id='filter_stage' name='stage'>
+                </select>
+                <select id='filter_difficulty' name='difficulty'>
+                </select>
+            </td>
+        </tr>
+    </table>
+    <div id='filter_apply'>
+        <input type='submit' value='Apply'/>
+    </div>
+</form>

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

@@ -42,6 +42,9 @@
             <a href='/<?=$UID?>/runs/'>
                 Runs
             </a>
+            <a href='/<?=$UID?>/stats/'>
+                Stats
+            </a>
             <a href='/<?=$UID?>/teams/'>
                 Teams
             </a>

+ 1 - 0
application/view/monster.php

@@ -421,6 +421,7 @@
                                         <span class='description <?=$aquired?>'>
                                             <?=$level->description?>
                                         </span>
+                                        <br/>
 <?php
                                     }
                                 }

+ 164 - 63
application/view/monster_info.php

@@ -155,7 +155,7 @@
 <?php
                                     foreach ($page->monster[$i]->essences as $essence){
 ?>
-                                       <div class='essence border_<?=$essence["element"]?>'>
+                                       <div class='essence border_<?=explode(" ", $essence["item"]->name)[2]?>'>
                                             <img alt='<?=$essence["item"]->name?>' title='<?=$essence["amount"]?>x <?=$essence["item"]->name?>' class='essence' src='<?=$essence["item"]->get_image()?>'/>
                                             <span>
                                                 <?=$essence["amount"]?>
@@ -190,6 +190,25 @@
                                         <span class='skill_level'>
                                             &nbsp;
                                         </span>
+                                    </td>
+<?php
+                                }
+                                $skill_num = 0;
+                                foreach ($page->monster[$i]->skill as $skill){
+                                    $skill_num ++;
+?>
+                                    <td class='skill'>
+                                        <img class='skill_img' src='<?=$skill->get_image()?>'/>
+                                    </td>
+<?php
+                                }
+?>
+                            </tr>
+                            <tr>
+<?php
+                                if ($page->monster[$i]->leader_skill){
+?>
+                                    <td>
                                         <span class='skill_name'>
                                             Leader skill
                                         </span>
@@ -201,77 +220,107 @@
                                 }
                                 foreach ($page->monster[$i]->skill as $skill){
 ?>
-                                    <td class='skill'>
-                                        <img class='skill_img' src='<?=$skill->get_image()?>'/>
+                                    <td>
                                         <span class='skill_name'>
                                             <?=$skill->name?>
                                         </span>
                                         <span class='skill_description'>
                                             <?=$skill->description?>
                                         </span>
+                                    </td>
 <?php
-                                        if (strlen($skill->multiplier_formula) > 0){
+                                }
 ?>
-                                            <span class='skill_formula'>
-                                                <?=$skill->multiplier_formula?>
-                                            </span>
+                            </tr>
+                            <tr>
 <?php
-                                        }
-                                        if (count($skill->effect) > 0){
+                                if ($page->monster[$i]->leader_skill){
 ?>
-                                            <span class='skill_effects'>
+                                    <td></td>
 <?php
-                                                foreach ($skill->effect as $effect){
+                                }
+                                foreach ($page->monster[$i]->skill as $skill){
 ?>
-                                                    <img class='effect_img' title='<?=$effect->name?>' src='<?=$effect->get_image()?>'/>
+                                    <td>
+                                        <span class='skill_formula'>
+                                            <?=$skill->multiplier_formula?>
+                                        </span>
+                                    </td>
 <?php
-                                                }
+                                }
 ?>
-                                            </span>
+                            </tr>
+                            <tr>
 <?php
-                                        }
-                                        if ($skill->max_level > 1){
+                                if ($page->monster[$i]->leader_skill){
 ?>
-                                            <table class='skill_up'>
+                                    <td></td>
 <?php
-                                                foreach ($skill->level as $level){
-                                                    if ($level->level > 1){
+                                }
+                                foreach ($page->monster[$i]->skill as $skill){
 ?>
-                                                        <tr>
-                                                            <td class='level'>
-                                                                Lv. <?=$level->level?>
-                                                            </td>
-                                                            <td class='description'>
-                                                                <?=$level->description?>
-                                                            </td>
-                                                        </tr>
+                                    <td>
+                                        <span class='skill_effects'>
 <?php
-                                                    }
-                                                }
+                                            foreach ($skill->effect as $effect){
+?>
+                                                <img class='effect_img' title='<?=$effect->name?>' src='<?=$effect->get_image()?>'/>
+<?php
+                                            }
+?>
+                                        </span>
+                                    </td>
+<?php
+                                }
+?>
+                            </tr>
+                            <tr>
+<?php
+                                if ($page->monster[$i]->leader_skill){
 ?>
-                                            </table>
+                                    <td></td>
+<?php
+                                }
+                                $skill_num = 0;
+                                foreach ($page->monster[$i]->skill as $skill){
+?>
+                                    <td class='skill_up'>
+<?php
+                                        foreach ($skill->level as $level){
+                                            if ($level->level > 1){
+?>
+                                                <span class='level'>
+                                                    Lv. <?=$level->level?>
+                                                </span>
+                                                <span class='description'>
+                                                    <?=$level->description?>
+                                                </span>
+                                                <br/>
 <?php
+                                            }
                                         }
 ?>
                                     </td>
 <?php
+                                    $skill_num ++;
                                 }
 ?>
                             </tr>
-                        </table> <!-- #skills -->
+                        </table>
+
 <?php
                         if ($page->monster[$i]->transformation != null){
 ?>
-                            <table class='skills'>
+                            <table class='skills skills_transformation'>
                                 <tr>
 <?php
-                                    $colspan = sizeof($page->monster[$i]->skill);
+                                    $colspan = sizeof($page->monster[$i]->transformation->skill);
                                     if ($page->monster[$i]->leader_skill){
                                         $colspan ++;
                                     }
 ?>
                                     <th colspan='<?=$colspan?>'>
-                                        Skills (transformation)
+                                        Skills (transformed)
                                     </th>
                                 </tr>
                                 <tr>
@@ -283,6 +332,29 @@
                                             <span class='skill_level'>
                                                 &nbsp;
                                             </span>
+                                        </td>
+<?php
+                                    }
+                                    $skill_num = 0;
+                                    $absolute_level = 0;
+                                    $absolute_max_level = 0;
+                                    $ponderated_level = 0;
+                                    $ponderated_max_level = 0;
+                                    foreach ($page->monster[$i]->transformation->skill as $skill){
+                                        $skill_num ++;
+?>
+                                        <td class='skill'>
+                                            <img class='skill_img' src='<?=$skill->get_image()?>'/>
+                                        </td>
+<?php
+                                    }
+?>
+                                </tr>
+                                <tr>
+<?php
+                                    if ($page->monster[$i]->leader_skill){
+?>
+                                        <td>
                                             <span class='skill_name'>
                                                 Leader skill
                                             </span>
@@ -294,60 +366,89 @@
                                     }
                                     foreach ($page->monster[$i]->transformation->skill as $skill){
 ?>
-                                        <td class='skill'>
-                                            <img class='skill_img' src='<?=$skill->get_image()?>'/>
+                                        <td>
                                             <span class='skill_name'>
                                                 <?=$skill->name?>
                                             </span>
                                             <span class='skill_description'>
                                                 <?=$skill->description?>
                                             </span>
+                                        </td>
 <?php
-                                            if (strlen($skill->multiplier_formula) > 0){
+                                    }
 ?>
-                                                <span class='skill_formula'>
-                                                    <?=$skill->multiplier_formula?>
-                                                </span>
+                                </tr>
+                                <tr>
 <?php
-                                            }
-                                            if (count($skill->effect) > 0){
+                                    if ($page->monster[$i]->leader_skill){
 ?>
-                                                <span class='skill_effects'>
+                                        <td></td>
 <?php
-                                                    foreach ($skill->effect as $effect){
+                                    }
+                                    foreach ($page->monster[$i]->transformation->skill as $skill){
 ?>
-                                                        <img class='effect_img' title='<?=$effect->name?>' src='<?=$effect->get_image()?>'/>
+                                        <td>
+                                            <span class='skill_formula'>
+                                                <?=$skill->multiplier_formula?>
+                                            </span>
+                                        </td>
 <?php
-                                                    }
+                                    }
 ?>
-                                                </span>
+                                </tr>
+                                <tr>
 <?php
-                                            }
-                                            if ($skill->max_level > 1){
+                                    if ($page->monster[$i]->leader_skill){
+?>
+                                        <td></td>
+<?php
+                                    }
+                                    foreach ($page->monster[$i]->transformation->skill as $skill){
+?>
+                                        <td>
+                                            <span class='skill_effects'>
+<?php
+                                                foreach ($skill->effect as $effect){
+?>
+                                                    <img class='effect_img' title='<?=$effect->name?>' src='<?=$effect->get_image()?>'/>
+<?php
+                                                }
+?>
+                                            </span>
+                                        </td>
+<?php
+                                    }
 ?>
-                                                <table class='skill_up'>
+                                </tr>
+                                <tr>
+<?php
+                                    if ($page->monster[$i]->leader_skill){
+?>
+                                        <td></td>
 <?php
-                                                    foreach ($skill->level as $level){
-                                                        if ($level->level > 1){
+                                    }
+                                    $skill_num = 0;
+                                    foreach ($page->monster[$i]->transformation->skill as $skill){
 ?>
-                                                            <tr>
-                                                                <td class='level'>
-                                                                    Lv. <?=$level->level?>
-                                                                </td>
-                                                                <td class='description'>
-                                                                    <?=$level->description?>
-                                                                </td>
-                                                            </tr>
+                                        <td class='skill_up'>
 <?php
-                                                        }
-                                                    }
+                                            foreach ($skill->level as $level){
+                                                if ($level->level > 1){
 ?>
-                                                </table>
+                                                    <span class='level'>
+                                                        Lv. <?=$level->level?>
+                                                    </span>
+                                                    <span class='description'>
+                                                        <?=$level->description?>
+                                                    </span>
+                                                    <br/>
 <?php
+                                                }
                                             }
 ?>
                                         </td>
 <?php
+                                        $skill_num ++;
                                     }
 ?>
                                 </tr>

+ 0 - 1
application/view/monsters.php

@@ -69,7 +69,6 @@
             <article id='filters_content'>
 <?php
                 $filters = $page->filters;
-                $custom_filters = $page->custom_filters;
                 include __DIR__ . "/inc/filter_monsters.php";
 ?>
             </article>

+ 1 - 0
application/view/report_runecraft.php

@@ -104,6 +104,7 @@
                                 }
 ?>
                             </div>
+                            <br/>
 <?php
                         }
 ?>

+ 0 - 1
application/view/report_skillup.php

@@ -68,7 +68,6 @@
             <article id='filters_content'>
 <?php
                 $filters = $page->filters;
-                $custom_filters = $page->custom_filters;
                 include __DIR__ . "/inc/filter_report_skillup.php";
 ?>
             </article>

+ 49 - 2
application/view/runs.php

@@ -95,9 +95,49 @@
                     slid.style.transform = 'rotate(0deg)';
                 }
             };
+
+            /**
+             * Shows the appropiate area selector.
+             */
+            function selectAreaType(){
+                var areaType = document.getElementById('filter_area_type').options[document.getElementById('filter_area_type').selectedIndex].value;
+                // Clear all selectors
+                var areas = document.getElementsByClassName('filter_area');
+                for (var i = 0; i < areas.length; i++) {
+                    areas[i].style.display = 'none';
+                    areas[i].removeAttribute('name');
+                }
+
+                // Show selected selector.
+                switch (areaType){
+                    case '<?=AREA_TYPE_ID::SCENARIO?>':
+                        document.getElementById('filter_area_scenario').style.display = 'inline-block';
+                        document.getElementById('filter_area_scenario').setAttribute('name', 'area');
+                        break;
+                    case '<?=AREA_TYPE_ID::CAIROS_DUNGEON?>':
+                        document.getElementById('filter_area_cairos').style.display = 'inline-block';
+                        document.getElementById('filter_area_cairos').setAttribute('name', 'area');
+                        break;
+                    case '<?=AREA_TYPE_ID::RIFT_DUNGEON?>':
+                        document.getElementById('filter_area_rift_dungeon').style.display = 'inline-block';
+                        document.getElementById('filter_area_rift_dungeon').setAttribute('name', 'area');
+                        break;
+                    // Rift Raid: Don't filter by area.
+                    // Arena: Don't filter by area.
+                    // Guild War: Don't filter by area.
+                    // Guild Siege: Don't filter by area.
+                    // Rift Raid: Don't filter by area.
+                    // Tartarus: Don't filter by area.
+                    // TOA: Don't filter by area.
+                    // Rift Raid: Don't filter by area.
+                    // WB: Don't filter by area.
+                    // Dimensional Rift: Don't filter by area.
+                    // TODO: Filter Dimensional Hole dungeon
+                }
+            }
         </script>
     </head>
-    <body>
+    <body onload='selectAreaType();'>
 <?php
         include __DIR__ . "/inc/header.php";
 ?>
@@ -141,7 +181,7 @@
                     foreach ($page->runs as $run){
                         $enable_save = true;
 ?>
-                        <tr>
+                        <tr id='run_<?=$run->id?>'>
                             <td class='area'>
                                 <div class='area'>
                                     <img title='<?=$run->area->name?>' class='area' src='<?=$run->area->get_image($run->stage)?>'/>
@@ -213,6 +253,13 @@
 ?>
                                     <div class='monster_panel <?=$disabled?>'>
                                         <?=HTML::unit_panel($party)?>
+<?php
+                                            if ($party->id == $run->leader){
+?>
+                                                <img alt='Leader' class='leader' src='<?=URL::IMG["ICON"]?>leader.png'/>
+<?php
+                                            }
+?>
                                     </div>
 <?php
                                     if ($disabled == ""){

+ 840 - 0
application/view/stats.php

@@ -0,0 +1,840 @@
+<?php
+    /**
+     * Runs view.
+     *
+     * Contains the view layout and ome code to present the data.
+     *
+     * @category View
+     * @property_read Stats_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?>stats.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>
+
+            /**
+             * Shows or hiddes the filters panel.
+             */
+            function toggleFilters(){
+                var content = document.getElementById('filters_content');
+                var slid = document.getElementById('filters_slid');
+                if (content.style.maxHeight != '30em'){
+                    content.style.maxHeight = '30em';
+                    slid.style.transform = 'rotate(90deg)';
+                }
+                else{
+                    content.style.maxHeight = '0px';
+                    slid.style.transform = 'rotate(0deg)';
+                }
+            };
+
+            function selectAreaType(){
+                var areaType = document.getElementById('filter_area_type').options[document.getElementById('filter_area_type').selectedIndex].value;
+                // Clear all selectors
+                document.getElementById('filter_stage').style.display = 'none';
+                document.getElementById('filter_difficulty').style.display = 'none';
+                var areas = document.getElementsByClassName('filter_area');
+                for (var i = 0; i < areas.length; i++) {
+                    areas[i].style.display = 'none';
+                    areas[i].removeAttribute('name');
+                }
+
+                // Show selected selector.
+                switch (areaType){
+                    case '<?=AREA_TYPE_ID::SCENARIO?>':
+                        document.getElementById('filter_area_scenario').style.display = 'inline-block';
+                        document.getElementById('filter_area_scenario').setAttribute('name', 'area');
+                        populateStage(7);
+                        document.getElementById('filter_stage').style.display = 'inline-block';
+                        populateDifficulty();
+                        document.getElementById('filter_difficulty').style.display = 'inline-block';
+                        break;
+                    case '<?=AREA_TYPE_ID::CAIROS_DUNGEON?>':
+                        document.getElementById('filter_area_cairos').style.display = 'inline-block';
+                        document.getElementById('filter_area_cairos').setAttribute('name', 'area');
+                        populateStage(10);
+                        document.getElementById('filter_stage').style.display = 'inline-block';
+                        break;
+                    case '<?=AREA_TYPE_ID::RIFT_DUNGEON?>':
+                        document.getElementById('filter_area_rift_dungeon').style.display = 'inline-block';
+                        document.getElementById('filter_area_rift_dungeon').setAttribute('name', 'area');
+                        break;
+                    // TODO: Other areas
+                    // Rift Raid: Don't filter by area.
+                    // Arena: Don't filter by area.
+                    // Guild War: Don't filter by area.
+                    // Guild Siege: Don't filter by area.
+                    // Rift Raid: Don't filter by area.
+                    // Tartarus: Don't filter by area.
+                    // TOA: Don't filter by area.
+                    // Rift Raid: Don't filter by area.
+                    // WB: Don't filter by area.
+                    // Dimensional Rift: Don't filter by area.
+                    // TODO: Filter Dimensional Hole dungeon
+                }
+            }
+
+            /**
+             * Shows the stage selector in the filters.
+             *
+             * @param total The number of stages to show.
+             */
+            function populateStage(total){
+                var sel = document.getElementById('filter_stage');
+                while (sel.firstChild) {
+                    sel.removeChild(sel.firstChild);
+                }
+                var opt;
+                var att;
+                // Empty
+                opt = document.createElement('option');
+                tex = document.createTextNode('');
+                opt.appendChild(tex);
+                sel.appendChild(opt);
+                for (var i = 1; i <= total; i ++){
+                    opt = document.createElement('option');
+                    tex = document.createTextNode('Stage ' + i);
+                    att = document.createAttribute('value');
+                    att.value = i;
+                    opt.setAttributeNode(att);
+                    opt.appendChild(tex);
+                    sel.appendChild(opt);
+                }
+            }
+
+            /**
+             * Shows the difficulty selector in the filter panel.
+             *
+             * @param labyrinth Indicates if the area type is the Tartarus
+             *        labyrinth. If so, it includes 'Easy'.
+             * @param toa Indicates if the area type is the Tower of
+             *        Ascension. If so, it excludes 'Hell'.
+             */
+            function populateDifficulty(labyrinth = false, toa = false){
+                var sel = document.getElementById('filter_difficulty');
+                while (sel.firstChild) {
+                    sel.removeChild(sel.firstChild);
+                }
+                var opt;
+                var att;
+                var tex;
+                // Empty
+                opt = document.createElement('option');
+                tex = document.createTextNode('');
+                opt.appendChild(tex);
+                sel.appendChild(opt);
+                // Easy (Labyrinth only)
+                if (labyrinth){
+                    opt = document.createElement('option');
+                    tex = document.createTextNode('Easy difficulty');
+                    att = document.createAttribute('value');
+                    att.value = '<?=DIFFICULTY_ID::EASY?>';
+                    opt.setAttributeNode(att);
+                    opt.appendChild(tex);
+                    sel.appendChild(opt);
+                }
+                // Normal
+                opt = document.createElement('option');
+                tex = document.createTextNode('Normal difficulty');
+                att = document.createAttribute('value');
+                att.value = '<?=DIFFICULTY_ID::NORMAL?>';
+                opt.setAttributeNode(att);
+                opt.appendChild(tex);
+                sel.appendChild(opt);
+                // Hard
+                opt = document.createElement('option');
+                tex = document.createTextNode('Hard difficulty');
+                att = document.createAttribute('value');
+                att.value = '<?=DIFFICULTY_ID::HARD?>';
+                opt.setAttributeNode(att);
+                opt.appendChild(tex);
+                sel.appendChild(opt);
+                // Hell
+                if (!toa){
+                    opt = document.createElement('option');
+                    tex = document.createTextNode('Hell difficulty');
+                    att = document.createAttribute('value');
+                    att.value = '<?=DIFFICULTY_ID::HELL?>';
+                    opt.setAttributeNode(att);
+                    opt.appendChild(tex);
+                    sel.appendChild(opt);
+                }
+            }
+
+            /**
+             * Sets the values for the stage and difficulty at page load.
+             */
+            function initialValues(){
+<?php
+                if ($page->filters["STAGE"] != null && $page->filters["STAGE"] > 0){
+?>
+                    document.getElementById('filter_stage').selectedIndex = <?=$page->filters["STAGE"]?>;
+<?php
+                }
+                if ($page->filters["DIFFICULTY"] != null && $page->filters["DIFFICULTY"] > 0){
+?>
+                    document.getElementById('filter_difficulty').selectedIndex = <?=$page->filters["DIFFICULTY"]?>;
+<?php
+                }
+?>
+            }
+
+
+            /**
+             * Calls all required functions to draw all diagrams.
+             */
+            function populateDiagrams(){
+<?php
+                if ($page->show_data){
+?>
+                    diagramWin();
+                    diagramDropType();
+                    diagramDropRuneSlot();
+                    diagramDropRuneStars();
+                    diagramDropRuneQuality();
+<?php
+                }
+?>
+            }
+
+            /**
+             * Generates the win rate diagram.
+             */
+            function diagramWin(){
+                var canvas = document.getElementById("diagram_win");
+                var ctx = canvas.getContext("2d");
+                ctx.lineWidth = 2;
+                var lastend = 0;
+                var data = [<?=$page->win_lose[0]?>, <?=$page->win_lose[1]?>];
+                var total = 0;
+                var color = [
+                    getComputedStyle(document.querySelector('div.diagram table.legend tr.color_1')).color,
+                    getComputedStyle(document.querySelector('div.diagram table.legend tr.color_2')).color
+                ];
+
+                for(var e = 0; e < data.length; e++){
+                  total += data[e];
+                }
+
+                for (var i = 0; i < data.length; i++) {
+                    ctx.beginPath();
+                    ctx.moveTo(canvas.width / 2, canvas.height / 2);
+                    ctx.arc(canvas.width / 2, canvas.height / 2, canvas.height / 2, lastend, lastend + (Math.PI * 2 * (data[i] / total)), false);
+                    ctx.lineTo(canvas.width / 2, canvas.height / 2);
+                    ctx.fillStyle = '#000000';
+                    ctx.stroke();
+                    ctx.fillStyle = color[i];
+                    ctx.fill();
+                    lastend += Math.PI * 2 * (data[i] / total);
+                }
+            }
+
+            /**
+             * Generates the drop rate diagram.
+             */
+            function diagramDropType(){
+                // TODO: Diferenciate scenario, dungeon and hall.
+                var canvas = document.getElementById("diagram_drop_type");
+                var ctx = canvas.getContext("2d");
+                ctx.lineWidth = 2;
+                var lastend = 0;
+                var data = [
+                    <?=$page->drop_type["rune"]?>,
+                    <?=$page->drop_type["rune_craft"]?>,
+                    <?=$page->drop_type["item"]?>,
+                    <?=$page->drop_type["sd"]?>,
+                    <?=$page->drop_type["shapeshifting"]?>,
+                    <?=$page->drop_type["unit"]?>,
+                ];
+                var total = 0;
+                var color = [
+                    getComputedStyle(document.querySelector('div.diagram table.legend tr.color_1')).color,
+                    getComputedStyle(document.querySelector('div.diagram table.legend tr.color_2')).color,
+                    getComputedStyle(document.querySelector('div.diagram table.legend tr.color_3')).color,
+                    getComputedStyle(document.querySelector('div.diagram table.legend tr.color_4')).color,
+                    getComputedStyle(document.querySelector('div.diagram table.legend tr.color_5')).color,
+                    getComputedStyle(document.querySelector('div.diagram table.legend tr.color_6')).color,
+                ];
+                for(var e = 0; e < data.length; e++){
+                    total += data[e];
+                }
+                for (var i = 0; i < data.length; i++) {
+                    ctx.beginPath();
+                    ctx.moveTo(canvas.width/2, canvas.height/2);
+                    ctx.arc(canvas.width/2, canvas.height/2, canvas.height/2, lastend, lastend + (Math.PI * 2 * (data[i] / total)), false);
+                    ctx.lineTo(canvas.width/2, canvas.height/2);
+                    ctx.fillStyle = '#000000';
+                    ctx.stroke();
+                    ctx.fillStyle = color[i];
+                    ctx.fill();
+                    lastend += Math.PI * 2 * (data[i] / total);
+                }
+            }
+
+            /**
+             * Generates the rune slot rate diagram.
+             */
+            function diagramDropRuneSlot(){
+                var canvas = document.getElementById("diagram_rune_slot");
+                var data = [
+                    <?=$page->drop_rune_slot["1"]?>,
+                    <?=$page->drop_rune_slot["2"]?>,
+                    <?=$page->drop_rune_slot["3"]?>,
+                    <?=$page->drop_rune_slot["4"]?>,
+                    <?=$page->drop_rune_slot["5"]?>,
+                    <?=$page->drop_rune_slot["6"]?>
+                ];
+                var total = 6;
+
+                // Polarchart base
+                var ctx_base = canvas.getContext("2d");
+                ctx_base.lineWidth = 1;
+                ctx_base.font = '32px serif'
+                ctx_base.textBaseline = 'middle'
+                ctx_base.textAlign = 'middle'
+                var lastend = Math.PI + Math.PI / total;
+                var radius = Math.round((canvas.width / 2) * ((i + 1) / total));
+                for (var i = 0; i < total; i++) {
+                    ctx_base.moveTo(canvas.width / 2, canvas.height / 2);
+                    ctx_base.arc(canvas.width / 2, canvas.height / 2, canvas.height / 2, lastend, lastend + (Math.PI * 2 / total), false);
+                    ctx_base.lineTo(canvas.width / 2, canvas.height / 2);
+                    ctx_base.strokeStyle = '#333333';
+                    ctx_base.stroke();
+                    lastend += (Math.PI * 2 / total);
+                }
+                ctx_base.fillStyle = '#33333366';
+                ctx_base.fill();
+                // Numbers
+                ctx_base.fillStyle = '#ffffff';
+                lastend = Math.PI + Math.PI / total;
+                for (var i = 0; i < total; i ++){
+                    lastend += (Math.PI * 2 / total);
+                    tx = canvas.width / 2 + canvas.width / 2.3 * Math.cos(lastend);
+                    ty = canvas.width / 2 + canvas.width / 2.3 * Math.sin(lastend);
+                    ctx_base.fillText(i + 1, tx - 8, ty);
+
+                }
+
+                // Polarchart content
+                var ctx_data = canvas.getContext("2d");
+                max = Math.max.apply(null, data)  * 1.1;
+                var initX = 0;
+                var initY = 0;
+                let region = new Path2D();
+                for (var i = 0; i < total; i++) {
+                    var r = Math.round((canvas.width / 2) * data[i] / max);
+                    var x = Math.round(r * Math.sin(Math.PI * 2 / total));
+                    var y = Math.round(r * Math.cos(Math.PI * 2 / total));
+
+                    // Rotate the point
+                    angle = (i + 4) * Math.PI * 2 / total;
+                    rx = x * Math.cos(angle) - y * Math.sin(angle)
+                    ry = y * Math.cos(angle) + x * Math.sin(angle)
+                    rx += canvas.width / 2;
+                    ry += canvas.width / 2;
+                    if (i == 0){
+                        region.moveTo(rx, ry);
+                        initX = rx;
+                        initY = ry;
+                    }
+                    else{
+                        region.lineTo(rx, ry);
+                    }
+                }
+                region.lineTo(initX, initY);
+                ctx_data.lineWidth = 2;
+                ctx_data.strokeStyle = '#ff0000';
+                ctx_data.stroke(region)
+                ctx_data.fillStyle = '#ff000055';
+                ctx_data.fill(region)
+            }
+
+            /**
+             * Generates the rune star rate diagram.
+             */
+            function diagramDropRuneStars(){
+                var canvas = document.getElementById("diagram_rune_stars");
+                var data = [
+                    <?=$page->drop_rune_stars["6"]?>,
+                    <?=$page->drop_rune_stars["5"]?>,
+                    <?=$page->drop_rune_stars["4"]?>,
+                    <?=$page->drop_rune_stars["3"]?>,
+                    <?=$page->drop_rune_stars["2"]?>,
+                    <?=$page->drop_rune_stars["1"]?>
+                ];
+                var total = 6;
+
+                // Polarchart base
+                var ctx_base = canvas.getContext("2d");
+                ctx_base.lineWidth = 1;
+                ctx_base.font = '32px serif'
+                ctx_base.textBaseline = 'middle'
+                ctx_base.textAlign = 'middle'
+                var lastend = Math.PI + Math.PI / total;
+                for (var i = 0; i < total; i++) {
+                    radius = Math.round((canvas.width / 2) * ((i + 1) / total));
+                    ctx_base.moveTo(canvas.width / 2, canvas.height / 2);
+                    ctx_base.arc(canvas.width / 2, canvas.height / 2, canvas.height / 2, lastend, lastend + (Math.PI * 2 / total), false);
+                    ctx_base.lineTo(canvas.width / 2, canvas.height / 2);
+                    ctx_base.strokeStyle = '#333333';
+                    ctx_base.stroke();
+                    lastend += (Math.PI * 2 / total);
+                }
+                ctx_base.fillStyle = '#33333366';
+                ctx_base.fill();
+                // Numbers
+                ctx_base.fillStyle = '#ffffff';
+                lastend = Math.PI + Math.PI / total;
+                for (var i = 0; i < total; i ++){
+                    lastend += (Math.PI * 2 / total);
+                    tx = canvas.width / 2 + canvas.width / 2.3 * Math.cos(lastend);
+                    ty = canvas.width / 2 + canvas.width / 2.3 * Math.sin(lastend);
+                    ctx_base.fillText(6 - i, tx - 8, ty);
+
+                }
+
+                // Polarchart content
+                var ctx_data = canvas.getContext("2d");
+                max = Math.max.apply(null, data) * 1.1;
+                var initX = 0;
+                var initY = 0;
+                let region = new Path2D();
+                for (var i = 0; i < total; i++) {
+                    var r = Math.round((canvas.width / 2) * data[i] / max);
+                    var x = Math.round(r * Math.sin(Math.PI * 2 / total));
+                    var y = Math.round(r * Math.cos(Math.PI * 2 / total));
+
+                    // Rotate the point
+                    angle = (i + 4) * Math.PI * 2 / total;
+                    rx = x * Math.cos(angle) - y * Math.sin(angle)
+                    ry = y * Math.cos(angle) + x * Math.sin(angle)
+                    rx += canvas.width / 2;
+                    ry += canvas.width / 2;
+                    if (i == 0){
+                        region.moveTo(rx, ry);
+                        initX = rx;
+                        initY = ry;
+                    }
+                    else{
+                        region.lineTo(rx, ry);
+                    }
+                }
+                region.lineTo(initX, initY);
+                ctx_data.lineWidth = 2;
+                ctx_data.strokeStyle = '#ff0000';
+                ctx_data.stroke(region)
+                ctx_data.fillStyle = '#ff000055';
+                ctx_data.fill(region)
+            }
+
+            /**
+             * Generates the rune quality rate diagram.
+             */
+            function diagramDropRuneQuality(){
+                var canvas = document.getElementById("diagram_rune_quality");
+                var data = [
+                    <?=$page->drop_rune_quality[strval(QUALITY_ID::LEGEND)]?>,
+                    <?=$page->drop_rune_quality[strval(QUALITY_ID::HERO)]?>,
+                    <?=$page->drop_rune_quality[strval(QUALITY_ID::RARE)]?>,
+                    <?=$page->drop_rune_quality[strval(QUALITY_ID::MAGIC)]?>,
+                    <?=$page->drop_rune_quality[strval(QUALITY_ID::NORMAL)]?>
+                ];
+                var total = 5;
+
+                // Polarchart base
+                var ctx_base = canvas.getContext("2d");
+                ctx_base.lineWidth = 1;
+                //var lastend = Math.PI * 2 / (0.5 * total / Math.PI);
+                var lastend = ((270 - (180 / (total))) * Math.PI / 180);
+                var color = [
+                    '#f5790066', // LEGEND
+                    '#ad7fa866', // HERO
+                    '#15c6e166', // RARE
+                    '#55ff5566', // MAGIC
+                    '#cccccc66'  // NORMAL
+                ];
+                for (var i = 0; i < total; i++) {
+                    ctx_base.beginPath();
+                    radius = Math.round((canvas.width / 2) * ((i + 1) / total));
+                    ctx_base.moveTo(canvas.width / 2, canvas.height / 2);
+                    ctx_base.arc(canvas.width / 2, canvas.height / 2, canvas.height / 2, lastend, lastend + (Math.PI * 2 / total), false);
+                    ctx_base.lineTo(canvas.width / 2, canvas.height / 2);
+                    ctx_base.fillStyle = color[i];
+                    ctx_base.fill();
+                    ctx_base.strokeStyle = '#333333';
+                    ctx_base.stroke();
+                    lastend += (Math.PI * 2 / total);
+                }
+                //ctx_base.fillStyle = '#33333366';
+                //ctx_base.fill()
+
+                // Polarchart content
+                var ctx_data = canvas.getContext("2d");
+                max = Math.max.apply(null, data) * 1.1;
+                var initX = 0;
+                var initY = 0;
+                let region = new Path2D();
+                for (var i = 0; i < total; i++) {
+                    var r = Math.round((canvas.width / 2) * data[i] / max);
+                    var x = Math.round(r * Math.sin(Math.PI * 2 / total));
+                    var y = Math.round(r * Math.cos(Math.PI * 2 / total));
+
+                    // Rotate the point
+                    // TOTALLY experimental, not reliable for a different number of sectors.
+                    angle = (i * Math.PI * 2 / total) + 2 * Math.PI / (2 * (total - 1)) + 270 * (4.6/6) * Math.PI / 180;
+                    rx = x * Math.cos(angle) - y * Math.sin(angle)
+                    ry = y * Math.cos(angle) + x * Math.sin(angle)
+                    rx += canvas.width / 2;
+                    ry += canvas.width / 2;
+                    if (i == 0){
+                        region.moveTo(rx, ry);
+                        initX = rx;
+                        initY = ry;
+                    }
+                    else{
+                        region.lineTo(rx, ry);
+                    }
+                }
+                region.lineTo(initX, initY);
+                ctx_data.lineWidth = 2;
+                ctx_data.strokeStyle = '#ff0000';
+                ctx_data.stroke(region)
+                ctx_data.fillStyle = '#ff000055';
+                ctx_data.fill(region)
+            }
+
+            // TODO: Script to show/hide area selector
+        </script>
+    </head>
+    <body onload='populateDiagrams();selectAreaType();initialValues();'>
+<?php
+        include __DIR__ . "/inc/header.php";
+?>
+        <section class='filters'>
+            <h2 id='filters_title' onClick='toggleFilters();'>
+                <img id='filters_slid' id='filters_slid' alt=' ' src='<?=URL::IMG["ICON"]?>slid.png'/>
+                Stat filters
+            </h2>
+            <article id='filters_content'>
+<?php
+                $filters = $page->filters;
+                include __DIR__ . "/inc/filter_stats.php";
+?>
+            </article>
+
+        </section> <!-- #filters -->
+<?php
+        if ($page->show_data){
+?>
+            <section class='content'>
+                <h2>
+                    Stats
+                </h2>
+                <article>
+                    <div id='win' class='diagram'>
+                        <h3>
+                            Victory rate
+                        </h3>
+                        <canvas id='diagram_win' width='200' height='200'></canvas>
+<?php
+                        $total = $page->win_lose[0] + $page->win_lose[1];
+                        // Fix divisions by 0 (dont use in total count):
+                        if ($total == 0){
+                            $total ++;
+                        }
+?>
+                        <table class='legend'>
+                            <tr class='color_1'>
+                                <td>
+                                    Victories:
+                                </td>
+                                <td>
+                                    <?=$page->win_lose[0]?>
+                                </td>
+                                <td>
+                                    (<?=number_format(($page->win_lose[0] * 100) / $total, 2, ".", ",")?>%)
+                                </td>
+                            </tr>
+                            <tr class='color_2'>
+                                <td>
+                                    Defeats:
+                                </td>
+                                <td>
+                                    <?=$page->win_lose[1]?>
+                                </td>
+                                <td>
+                                    (<?=number_format(($page->win_lose[1] * 100) / $total, 2, ".", ",")?>%)
+                                </td>
+                            </tr>
+                            <tr class='color_0'>
+                                <td>
+                                    Total:
+                                </td>
+                                <td colspan='2'>
+                                    <?=($page->win_lose[0] + $page->win_lose[1])?>
+                                </td>
+                            </tr>
+                        </table>
+                    </div>
+                    <div id='drop_type' class='diagram'>
+                        <h3>
+                            Drop rate
+                        </h3>
+                        <canvas id='diagram_drop_type' width='200' height='200'></canvas>
+                        <table class='legend'>
+<?php
+                            // Do some calculations
+                            $rune = $page->drop_type["rune"];
+                            $rucr = $page->drop_type["rune_craft"];
+                            $item = $page->drop_type["item"];
+                            $sedu = $page->drop_type["sd"];
+                            $shap = $page->drop_type["shapeshifting"];
+                            $unit = $page->drop_type["unit"];
+                            $total = $rune + $rucr + $item + $sedu + $shap + $unit;
+                            // Fix divisions by 0:
+                            if ($total == 0){
+                                $total ++;
+                            }
+?>
+                            <tr class='color_1'>
+                                <td>
+                                    Rune:
+                                </td>
+                                <td>
+                                    <?=$rune?>
+                                </td>
+                                <td>
+                                    (<?=number_format(($rune * 100) / $total, 2, ".", ",")?>%)
+                                </td>
+                            </tr>
+                            <tr class='color_2'>
+                                <td>
+                                    Rune craft item:
+                                </td>
+                                <td>
+                                    <?=$rucr?>
+                                </td>
+                                <td>
+                                    (<?=number_format(($rucr * 100) / $total, 2, ".", ",")?>%)
+                                </td>
+                            </tr>
+                            <tr class='color_3'>
+                                <td>
+                                    Item:
+                                </td>
+                                <td>
+                                    <?=$item?>
+                                </td>
+                                <td>
+                                    (<?=number_format(($item * 100) / $total, 2, ".", ",")?>%)
+                                </td>
+                            </tr>
+                            <tr class='color_4'>
+                                <td>
+                                    Secret Dungeon:
+                                </td>
+                                <td>
+                                    <?=$sedu?>
+                                </td>
+                                <td>
+                                    (<?=number_format(($sedu * 100) / $total, 2, ".", ",")?>%)
+                                </td>
+                            </tr>
+                            <tr class='color_5'>
+                                <td>
+                                    Transmog Stone:
+                                </td>
+                                <td>
+                                    <?=$shap?>
+                                </td>
+                                <td>
+                                    (<?=number_format(($shap * 100) / $total, 2, ".", ",")?>%)
+                                </td>
+                            </tr>
+                            <tr class='color_6'>
+                                <td>
+                                    Unit:
+                                </td>
+                                <td>
+                                    <?=$unit?>
+                                </td>
+                                <td>
+                                    (<?=number_format(($unit * 100) / $total, 2, ".", ",")?>%)
+                                </td>
+                            </tr>
+                        </table>
+                    </div>
+                    <div id='drop_rune_slot' class='diagram'>
+                        <h3>
+                            Rune drops by slot
+                        </h3>
+                        <canvas id='diagram_rune_slot' width='200' height='200'></canvas>
+                        <table class='legend'>
+<?php
+                            $total = array_sum($page->drop_rune_slot);
+                            // Fix divisions by 0:
+                            if ($total == 0){
+                                $total ++;
+                            }
+                            for ($i = 1; $i <= 6; $i ++){
+                                $val = $page->drop_rune_slot[strval($i)]
+?>
+                                <tr class='color_0'>
+                                    <td>
+                                        Slot <?=$i?>:
+                                    </td>
+                                    <td>
+                                         <?=$val?>
+                                    </td>
+                                    <td>
+                                        (<?=number_format(($val * 100) / $total, 2, ".", ",")?>%)
+                                    </td>
+                                </tr>
+<?php
+                            }
+?>
+                        </table>
+                    </div>
+                    <div id='drop_rune_stars' class='diagram'>
+                        <h3>
+                            Rune drops by stars
+                        </h3>
+                        <canvas id='diagram_rune_stars' width='200' height='200'></canvas>
+                        <table class='legend'>
+<?php
+                            $total = array_sum($page->drop_rune_stars);
+                            // Fix divisions by 0:
+                            if ($total == 0){
+                                $total ++;
+                            }
+                            for ($i = 6; $i >= 1; $i --){
+                                $val = $page->drop_rune_stars[strval($i)]
+?>
+                                <tr class='color_0'>
+                                    <td>
+<?php
+                                        for ($j = 1; $j <= $i; $j ++){
+?>
+                                            <img class='star star_gold' src='<?=URL::IMG["ICON"]?>star.png'/>
+<?php
+                                        }
+?>
+                                    </td>
+                                    <td>
+                                        <?=$val?>
+                                    </td>
+                                    <td>
+                                        (<?=number_format(($val * 100) / $total, 2, ".", ",")?>%)
+                                    </td>
+                                </tr>
+<?php
+                            }
+?>
+                        </table>
+                    </div>
+                    <div id='drop_rune_quality' class='diagram'>
+                        <h3>
+                            Rune drops by quality
+                        </h3>
+                        <canvas id='diagram_rune_quality' width='200' height='200'></canvas>
+                        <table class='legend'>
+<?php
+                            $total = array_sum($page->drop_rune_quality);
+                            // Fix divisions by 0:
+                            if ($total == 0){
+                                $total ++;
+                            }
+?>
+                            <tr class='color_legend'>
+                                <td>
+                                    Legend:
+                                </td>
+                                <td>
+                                    <?=$page->drop_rune_quality[QUALITY_ID::LEGEND]?>
+                                </td>
+                                <td>
+                                    (<?=number_format(($page->drop_rune_quality[QUALITY_ID::LEGEND] * 100) / $total, 2, ".", ",")?>%)
+                                </td>
+                            </tr>
+                            <tr class='color_hero'>
+                                <td>
+                                    Hero:
+                                </td>
+                                <td>
+                                    <?=$page->drop_rune_quality[QUALITY_ID::HERO]?>
+                                </td>
+                                <td>
+                                    (<?=number_format(($page->drop_rune_quality[QUALITY_ID::HERO] * 100) / $total, 2, ".", ",")?>%)
+                                </td>
+                            </tr>
+                            <tr class='color_rare'>
+                                <td>
+                                    Rare:
+                                </td>
+                                <td>
+                                    <?=$page->drop_rune_quality[QUALITY_ID::RARE]?>
+                                </td>
+                                <td>
+                                    (<?=number_format(($page->drop_rune_quality[QUALITY_ID::RARE] * 100) / $total, 2, ".", ",")?>%)
+                                </td>
+                            </tr>
+                            <tr class='color_magic'>
+                                <td>
+                                    Magic:
+                                </td>
+                                <td>
+                                    <?=$page->drop_rune_quality[QUALITY_ID::MAGIC]?>
+                                </td>
+                                <td>
+                                    (<?=number_format(($page->drop_rune_quality[QUALITY_ID::MAGIC] * 100) / $total, 2, ".", ",")?>%)
+                                </td>
+                            </tr>
+                            <tr class='color_normal'>
+                                <td>
+                                    Normal:
+                                </td>
+                                <td>
+                                    <?=$page->drop_rune_quality[QUALITY_ID::NORMAL]?>
+                                </td>
+                                <td>
+                                    (<?=number_format(($page->drop_rune_quality[QUALITY_ID::NORMAL] * 100) / $total, 2, ".", ",")?>%)
+                                </td>
+                            </tr>
+                        </table>
+                    </div>
+                </article>
+            </section>
+<?php
+        }
+        include __DIR__ . "/inc/footer.php";
+?>
+    </body>
+</html>

+ 152 - 43
application/view/teams.php

@@ -43,6 +43,9 @@
             max_units = 0;
             selected_units = 0;
             selected_ids = [];
+
+            current_slot_id = "";
+            leader_id = "";
 <?php
             foreach ($page->units as $unit){
 ?>
@@ -140,7 +143,40 @@
                     difficulty = 0;
                 }
 
-                var ids = selected_ids.join(',');
+                var request =
+                  'uid=<?=$UID?>' +
+                  '&area_type=' + area_type +
+                  '&area=' + area +
+                  '&difficulty=' + difficulty +
+                  '&stage=' + stage +
+                  '&name=' + encodeURI(name) +
+                  '&description=' + encodeURI(description) +
+                  '&leader=' + leader_id;
+
+                var id = [];
+                var front = 0;
+                var slots = document.getElementsByClassName('new_unit_slot');
+                for (var i = 0; i < slots.length; i ++) {
+                    slots[i].classList.remove('leader');
+                    var id_value = slots[i].getElementsByClassName('unit_id')[0].value;
+                    if (String(id_value).length > 0){
+                        id.push(id_value);
+                        if (
+                          (
+                            area_type == '<?=AREA_TYPE_ID::RIFT_DUNGEON?>' ||
+                            area_type == '<?=AREA_TYPE_ID::RIFT_RAID?>'
+                          ) &&
+                          i <= 3
+                        ){
+                            front = front + 1;
+                        }
+                    }
+                }
+                var ids = id.join(',');
+                request =
+                  request +
+                  '&ids=' + ids +
+                  '&front=' + front;
                 var xhttp = new XMLHttpRequest();
                 xhttp.onreadystatechange = function() {
                     if (this.readyState == 4){ 
@@ -154,7 +190,7 @@
                 };
                 xhttp.open("POST", "/action/new_team/", true);
                 xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
-                xhttp.send('uid=<?=$UID?>&ids=' + ids + '&area_type=' + area_type + '&area=' + area + '&difficulty=' + difficulty + '&stage=' + stage + '&name=' + encodeURI(name) + '&description=' + encodeURI(description));
+                xhttp.send(request);
 
             }
 
@@ -162,14 +198,14 @@
              * Searches a unit with the pattern entered in the 'new_unit_id'
              * field. Shows a clickable list of matching units.
              */
-            function searchUnit(){
+            function searchUnit(panel){
                 key = document.getElementById('new_unit_id').value;
                 var autocomplete = document.getElementById('new_unit_autocomplete');
                 while (autocomplete.firstChild) {
                     autocomplete.removeChild(autocomplete.firstChild);
                 }
                 if (key.length == 0){
-                    autocomplete.style.display = 'none';
+                    autocomplete.style.display = 'block';
                 }
                 for (var i = 0; i < units.length; i++) {
                     if (units[i].name.toUpperCase().includes(key.toUpperCase())){
@@ -189,6 +225,38 @@
                 }
             }
 
+            /**
+             * Shows the search bar to searcha unit below a slot.
+             */
+            function showSearchUnit(slot){
+                if (selected_units >= max_units){
+                    return;
+                }
+                var key = document.getElementById('new_unit_id');
+                key.style.left = (slot.offsetLeft + 5) + 'px';
+                key.style.top = (slot.offsetTop + 50) + 'px';
+                key.style.display = 'block';
+                key.focus();
+
+                var autocomplete = document.getElementById('new_unit_autocomplete');
+                autocomplete.style.left = key.left;
+                autocomplete.style.top = (key.offsetTop + 15) + 'px';
+
+                current_slot_id = slot.id;
+            }
+
+            /**
+             * Selects a leader unit.
+             */
+            function selectLeader(slot){
+                leader_id = slot.getElementsByClassName('unit_id')[0].value;
+                var slots = document.getElementsByClassName('new_unit_slot');
+                for (var i = 0; i < slots.length; i ++) {
+                    slots[i].classList.remove('leader');
+                }
+                slot.classList.add('leader');
+            }
+
             /**
              * Selects a unit from the list and adds it to the new team.
              * The search list is hidden afterwards.
@@ -214,10 +282,11 @@
                     }
                 }
                 if (name != null){
+
+                    // Build monster panel
                     var monsterPanel = document.createElement('div');
                     monsterPanel.setAttribute('class', 'monster_panel');
                     monsterPanel.setAttribute('id', 'monster_panel_' + id);
-                    monsterPanel.setAttribute('onclick', 'deleteUnit(' + id + ')');
                     var monsterStars = document.createElement('span');
                     monsterStars.setAttribute('class', 'stars');
                     for (var i = 0; i < stars; i ++){
@@ -236,8 +305,24 @@
                     monsterPanel.appendChild(monsterStars);
                     monsterPanel.appendChild(monsterImage);
                     monsterPanel.appendChild(monsterLevel);
-                    var teamPanel = document.getElementById('new_team_units');
-                    teamPanel.appendChild(monsterPanel);
+
+                    // Attach monster panel
+                    var slot = document.getElementById(current_slot_id);
+                    slot.appendChild(monsterPanel);
+                    slot.setAttribute('onclick', 'selectLeader(this);');
+
+                    // X to remove
+                    var x = document.createElement('input');
+                    x.setAttribute('type', 'button');
+                    x.setAttribute('value', 'x');
+                    x.setAttribute('class', 'delete_unit');
+                    x.setAttribute('onClick', 'deleteUnit(this.parentElement.parentElement);');
+                    monsterPanel.appendChild(x);
+
+                    // Set ID
+                    var input_id = slot.getElementsByClassName('unit_id')[0];
+                    input_id.value = id;
+
                     // Hide the selector
                     var autocomplete = document.getElementById('new_unit_autocomplete');
                     while (autocomplete.firstChild) {
@@ -245,41 +330,30 @@
                     }
                     autocomplete.style.display = 'none';
                     document.getElementById('new_unit_id').value = '';
+                    document.getElementById('new_unit_id').style.display = 'none';
                     selected_units ++;
-                    if (selected_units >= max_units){
-                        document.getElementById('new_unit').style.display = 'none';
-                    }
-                    else{
-                        document.getElementById('new_unit').style.display = 'block';
-                    }
-                    selected_ids.push(id);
                 }
             }
 
             /**
              * Deletes a unit from the new team form.
-             * 
-             * @param id Unit id.
              */
-            function deleteUnit(id){
-                var position = -1;
-                for (var i = 0; i < units.length; i ++) {
-                    if (units[i].id == id){
-                        position = i;
-                        break;
-                    }
-                }
-                if (position >= 0 && position < units.length){
-                    units.splice(position, 1);
-                    document.getElementById('new_team_units').removeChild(document.getElementById('monster_panel_' + id));
-                    selected_units --;
-                    if (selected_units >= max_units){
-                        document.getElementById('new_unit').style.display = 'none';
-                    }
-                    else{
-                        document.getElementById('new_unit').style.display = 'block';
-                    }
+            function deleteUnit(slot){
+            console.log(slot.nodename)
+                var panel = slot.getElementsByClassName('monster_panel')[0];
+                slot.removeChild(panel);
+                var id = slot.getElementsByClassName('unit_id')[0];
+                id.value = '';
+                selected_units --;
+
+                // Remove leader symbol
+                if (slot.classList.contains('leader')){
+                    slot.classList.remove('leader');
+                    leader_id = '';
                 }
+
+                slot.setAttribute('onClick', 'showSearchUnit(this);');
+
             }
 
             /**
@@ -374,6 +448,13 @@
              * selected in the new team panel.
              */
             function selectNewAreaType(){
+
+                max_units = 0;
+                //selected_units = 0;
+                //selected_ids = [];
+                current_slot_id = "";
+                leader_id = "";
+
                 var type = document.getElementById('new_area_type').options[document.getElementById('new_area_type').selectedIndex].value;
                 document.getElementById('new_area_scenario').style.display = 'none';
                 document.getElementById('new_area_scenario').selectedIndex = 0;
@@ -442,11 +523,37 @@
                         //document.getElementById('new_area_dimensional_rift').style.display = 'inline';
                         break;
                 }
-                if (selected_units >= max_units || type == ''){
-                    document.getElementById('new_unit').style.display = 'none';
+
+                var container = document.getElementById('new_team_units');
+                while (container.firstChild) {
+                    container.removeChild(container.firstChild);
                 }
-                else{
-                    document.getElementById('new_unit').style.display = 'block';
+
+                var panel;
+                var hr;
+                var id;
+                var max_slots = max_units;
+                for (var i = 0; i < max_slots; i ++){
+                    panel = document.createElement('div');
+                    panel.setAttribute('id', 'new_unit_slot_' + i);
+                    panel.setAttribute('class', 'new_unit_slot');
+                    panel.setAttribute('onClick', 'showSearchUnit(this);');
+                    id = document.createElement('input');
+                    id.setAttribute('type', 'hidden');
+                    id.setAttribute('class', 'unit_id');
+                    panel.appendChild(id);
+                    container.appendChild(panel);
+                    if (
+                      (
+                        type == '<?=AREA_TYPE_ID::RIFT_DUNGEON?>' ||
+                        type == '<?=AREA_TYPE_ID::RIFT_RAID?>'
+                      ) &&
+                      i == 3
+                    ){
+                        max_slots = 8;
+                        hr = document.createElement('hr');
+                        container.appendChild(hr);
+                    }
                 }
             }
 
@@ -750,14 +857,16 @@
                     <tr>
                         <td class='new_title'>
                             Units:
-                            <div class='new_unit' id='new_unit'>
-                                <input id='new_unit_id' onInput='searchUnit();'/>
-                                <div id='new_unit_autocomplete' class='new_unit_autocomplete'>
-                            </div>
+                            <!--<div class='new_unit' id='new_unit'>
+                                
+                                
+                            </div>-->
                         </td>
-                        <td class='new_values'>
+                        <td id='new_units' class='new_values'>
                             <div id='new_team_units'>
                             </div>
+                            <input id='new_unit_id' class='no_remove' onInput='searchUnit();'/>
+                            <div id='new_unit_autocomplete' class='no_remove new_unit_autocomplete'>
                         </td>
                     </tr>
                     <tr>

+ 19 - 6
public/css/monster.css

@@ -99,17 +99,29 @@ section#monster article#profile table#skills{
     margin: 1em auto;
     color: #ffffff;
     text-align: center;
-    width: 98%;
+    width: 90%;
     position: relative;
     border-collapse: collapse;
     table-layout: fixed;
+    border-top: 3px solid #c8ad78;
+    border-bottom: 3px solid #c8ad78;
 }
 
-section#monster article#profile table#skills th{border: none;}
+section#monster article#profile table#skills th{
+    border-left: 3px solid #c8ad78;
+    border-right: 3px solid #c8ad78;
+    border-bottom: 3px solid #c8ad78;
+    font-size: 150%;
+    padding: 0.5em;
+    background-color: #00000044;
+}
 
 section#monster article#profile table#skills td{
     vertical-align: top;
     padding: 0.2em 0.5em;
+    border-right: 3px solid #c8ad78;
+    border-left: 3px solid #c8ad78;
+    background-color: #ffffff33;
 }
 
 section#monster article#profile table#skills img.skill_img{
@@ -117,6 +129,7 @@ section#monster article#profile table#skills img.skill_img{
     height: 4em;
     border: 0.15em solid var(--ui-color-border);
     border-radius: 0.4em;
+    margin-top: 1em;
 }
 section#monster article#profile table#skills span.skill_level{
     font-size: 100%;
@@ -152,7 +165,7 @@ section#monster article#profile table#skills span.skill_formula{
     display: block;
     text-align: center;
     margin-top: 1em;
-    font-size: 50%;
+    font-size: 80%;
     color: #cccccc;
     font-weight: bold;
     font-style: italic;
@@ -165,8 +178,8 @@ section#monster article#profile table#skills span.skill_effects{
 }
 
 section#monster article#profile table#skills span.skill_effects img.effect_img{
-    width: 1.5em;
-    height: 1.5em;
+    width: 2em;
+    height: 2em;
     border: 0.1em solid #000000;
     border-radius: 0.5em;
 }
@@ -174,7 +187,7 @@ section#monster article#profile table#skills span.skill_effects img.effect_img{
 section#monster article#profile table#skills td.skill_up{
     margin: 0.5em auto 0 auto;
     border-collapse: collapse;
-    font-size: 70%;
+    font-size: 80%;
     color: #999999;
     font-style: italic;
 }

+ 49 - 51
public/css/monster_info.css

@@ -35,7 +35,7 @@ article.stage div.profile{
 }
 
 article.stage table.stats{
-    font-size: 65%;
+    font-size: 75%;
     border: 0.1em solid black;
     color: #ffffff;
     margin: 0.4em;
@@ -46,7 +46,7 @@ article.stage table.stats{
     max-width: 10em;
 }
 
-article.stage table.stats td,th{
+article.stage table.stats td, article.stage table.stats th{
     padding: 0.01em 0.5em;
     border: 0.1em solid #000000;
 }
@@ -114,38 +114,52 @@ article.stage div.extra div.essences div.essence span{
 }
 
 article.stage table.skills{
-    width: 100%;
+    display: block;
+    vertical-align: top;
+    margin: 1em auto;
+    color: #ffffff;
     text-align: center;
-    margin: 1em 0 0 0;
-    padding: 0;
+    width: 90%;
+    position: relative;
+    border-collapse: collapse;
     table-layout: fixed;
+    border-top: 3px solid #c8ad78;
+    border-bottom: 3px solid #c8ad78;
 }
 
-article.stage table.skills tr th{border: none;}
+article.stage table.skills th{
+    border-left: 3px solid #c8ad78;
+    border-right: 3px solid #c8ad78;
+    border-bottom: 3px solid #c8ad78;
+    font-size: 150%;
+    padding: 0.5em;
+    background-color: #00000066;
+}
 
-article.stage table.skills tr td.skill{
+article.stage table.skills td{
     vertical-align: top;
-    margin: 0.2em;
-    color: #ffffff;
-    text-align: center;
-    max-width: 20%;
+    padding: 0.2em 0.5em;
+    border-right: 3px solid #c8ad78;
+    border-left: 3px solid #c8ad78;
+    background-color: #ffffff33;
 }
 
-article.stage table.skills tr td.skill img.skill_img{
-    width: 3em;
-    height: 3em;
+article.stage table.skills img.skill_img{
+    width: 4em;
+    height: 4em;
     border: 0.15em solid var(--ui-color-border);
-    border-radius: 0.5em;
+    border-radius: 0.4em;
+    margin-top: 1em;
 }
 
-article.stage table.skills tr td.skill span.skill_name{
+article.stage table.skills span.skill_name{
     text-shadow: 0 0 0.2em #000000, 0 0 0.2em #000000, 0 0 0.4em #000000;
+    margin: 0 0 0.5em 0;
     display: block;
     font-weight: bold;
-    font-size: 90%;
 }
 
-article.stage table.skills tr td.skill span.skill_description{
+article.stage table.skills span.skill_description{
     font-size: 80%;
     font-style: italic;
     margin: 0;
@@ -154,60 +168,44 @@ article.stage table.skills tr td.skill span.skill_description{
     line-height: 0.95em;
 }
 
-article.stage table.skills tr td.skill span.skill_formula{
+article.stage table.skills span.skill_formula{
     display: block;
     text-align: center;
     margin-top: 1em;
-    font-size: 50%;
+    font-size: 80%;
     color: #cccccc;
     font-weight: bold;
     font-style: italic;
     font-family: monospace, monospace;
 }
 
-article.stage table.skills tr td.skill span.skill_effects{
+article.stage table.skills span.skill_effects{
     display: block;
     text-align: center;
-    margin-top: 0.5em;
 }
 
-article.stage table.skills tr td.skill span.skill_effects img.effect_img{
-    width: 1.5em;
-    height: 1.5em;
+article.stage table.skills span.skill_effects img.effect_img{
+    width: 2em;
+    height: 2em;
     border: 0.1em solid #000000;
     border-radius: 0.5em;
 }
 
-article.stage table.skills tr td.skill table.skill_up{
-    font-size: 70%;
+article.stage table.skills td.skill_up{
     margin: 0.5em auto 0 auto;
     border-collapse: collapse;
+    font-size: 80%;
+    color: #efefef;
+    font-style: italic;
 }
 
-
-article.stage table.skills tr td.skill table.skill_up td{
-    font-size: 90%;
-    padding: 0.2em;
-    border: 0 solid #000000;
-    line-height: 1em;
-}
-@media screen and (max-width : 990px){
-    article.stage table.skills tr td.skill table.skill_up td{
-        padding: 0;
-        border: 0.1em solid black;
-        line-height: 0.95em;
-        font-size: 70%;
-    }
+article.stage table.skills td.skill_up span.level{
+    width: 3em;
+    display: inline-block;
 }
-
-article.stage table.skills tr td.skill table.skill_up td.level{
+article.stage table.skills td.skill_up span.description{
     font-style: italic;
-    padding-right: 0.8em;
-    min-width: 2em;
-    font-size: 80%
-}
-@media screen and (max-width : 990px){
-    article.stage table.skills tr td.skill table.skill_up td.level{
-        font-size: 70%
-    }
+    min-width: 6em;
+    display: inline-block;
+    width: 70%;
 }

+ 25 - 11
public/css/runs.css

@@ -8,16 +8,12 @@ table#runs td.area, td.date, td.team, td.time, td.reward, td.action{
     border-top: 0.1em solid #99999977;
 }
 
-table#runs td.area div.area{
-    position: relative;
-    width: 2em;
-    height: 2em;
+table#runs td.area{
+    text-align: center;
+    width: 4em;
 }
 
 table#runs td.area div.area img.area{
-    position: absolute;
-    top: 0;
-    left: 0;
     width: 2em;
     height: 2em;
     border: 0.2em solid #555555;
@@ -26,10 +22,10 @@ table#runs td.area div.area img.area{
 }
 
 table#runs td.area div.area span.stage{
-    position: absolute;
-    top: 1.3em;
-    left: 0;
-    width: 2em;
+    position: relative;
+    left: -1.85em;
+    top: -0.4em;
+    font-size: 80%;
     text-align: right;
     text-shadow: 0 0 0.3em #000, 0 0 0.3em #000, 0 0 0.3em #000, 0 0 0.2em #000, 0 0 0.1em #000, 0 0 0.1em #000;
     pointer-events: none;
@@ -56,6 +52,7 @@ table#runs td.date{
 
 table#runs td.team{
     font-size: 30%;
+    width: 46em;
 }
 
 table#runs td.team div.helper{
@@ -65,6 +62,23 @@ table#runs td.team div.helper{
 
 table#runs td.team div.monster_panel.disabled{
     opacity: 0.5;
+    vertical-align: top;
+}
+
+table#runs td.team div.monster_panel img.leader{
+    width: 5em;
+    height: 5em;
+    display: block;
+    position: absolute;
+    top: -1.7em;
+    left: -0.7em;
+}
+
+table#runs td.team hr.frontline{
+    display: block;
+    width: 46em;
+    margin: -0.6em 0 0.2em 0;
+    color: #666666;
 }
 
 table#runs td.time{

+ 51 - 0
public/css/stats.css

@@ -0,0 +1,51 @@
+div.diagram{
+    display: inline-block;
+    vertical-align: top;
+    text-align: center;
+    border: 0.2em solid #c8ad78;
+    background-color: #a4886c;
+    border-radius: 0.5em;
+    margin: 0.5em;
+    width: 275px;
+    height: 400px;
+}
+
+div.diagram h3{
+    border-top-left-radius: 0.5em;
+    border-top-right-radius: 0.5em;
+    border-bottom: 0.1em solid #c8ad78;
+    margin: 0 0 0.5em 0;
+    padding: 0.5em;
+    background-color: #93775b;
+}
+
+div.diagram table.legend{
+    list-style: none;
+    text-align: left;
+    font-weight: bold;
+    width: 100%;
+    font-size: 90%;
+}
+
+div.diagram table.legend tr.color_0{color: #ffffff;}
+div.diagram table.legend tr.color_1{color: #33ff33;}
+div.diagram table.legend tr.color_2{color: #ff3333;}
+div.diagram table.legend tr.color_3{color: #6666ff;}
+div.diagram table.legend tr.color_4{color: #ffff33;}
+div.diagram table.legend tr.color_5{color: #ff33ff;}
+div.diagram table.legend tr.color_6{color: #33ffff;}
+div.diagram table.legend tr.color_normal{color: #cccccc;}
+div.diagram table.legend tr.color_magic{color: #55ff55;}
+div.diagram table.legend tr.color_rare{color: #15c6e1;}
+div.diagram table.legend tr.color_hero{color: #ad7fa8;}
+div.diagram table.legend tr.color_legend{color: #f57900;}
+div.diagram table.legend td{
+    padding-right: 1em;
+    text-shadow: 0 0 0.1em #000000;
+}
+
+div#drop_rune_stars img.star{
+    width: 1em;
+    height: 1em;
+    margin-right: -0.5em;
+}

+ 55 - 5
public/css/teams.css

@@ -121,7 +121,14 @@ section#new_team div#new_team_units{
 
 section#new_team div#new_team_units div.monster_panel{
     font-size: 50%;
-    cursor: not-allowed;
+    cursor: pointer;
+}
+
+section#new_team div#new_team_units div.monster_panel input.delete_unit{
+    position: absolute;
+    left: 0.1em;
+    bottom: 0.1em;
+    pointer-events: initial;
 }
 
 section#new_team div.new_unit{
@@ -129,15 +136,58 @@ section#new_team div.new_unit{
     display: none;
 }
 
-section#new_team div.new_unit div.new_unit_autocomplete{
+section#new_team div.new_unit{
+    text-align: right;
+    display: none;
+}
+
+section#new_team div#new_team_units div.new_unit_slot{
+    border: 0.15em solid var(--ui-color-border);
+    border-radius: 0.6em;
+    background-color: #ffffff55;
+    width: 4em;
+    height: 4em;
+    display: inline-block;
+    vertical-align: top;
+    cursor: pointer;
+    margin: 0.1em;
+}
+
+section#new_team div#new_team_units div.new_unit_slot.leader{
+    border: 0.15em solid #ff0000;
+}
+section#new_team div#new_team_units div.new_unit_slot.leader:before{
+    content: " L";
+    color: #ffffff;
+    background-color: #ff0000;
+    position: absolute;
+    z-index: 20;
+    border-top-left-radius: 0.15em;
+    border-bottom-right-radius: 0.5em;
+    display: block;
+    padding: 0.1em 0.3em;
+    font-weight: bold;
+}
+
+section#new_team td#new_units{
+    position: relative;
+}
+
+section#new_team input#new_unit_id{
+    display: none;
+    position: absolute;
+}
+
+section#new_team div#new_unit_autocomplete{
     border: 0.1em solid #000000;
     display: none;
     border-radius: 0.5em;
     margin: auto auto auto 1em;
     position: absolute;
+    z-index: 40;
 }
 
-section#new_team div.new_unit div.new_unit_autocomplete div{
+section#new_team div#new_unit_autocomplete div{
     border: 0.05em solid #000000;
     text-align: left;
     text-transform: capitalize;
@@ -153,11 +203,11 @@ section#new_team div.new_unit div.new_unit_autocomplete div{
     box-shadow: var(--input-box-shadow);
 }
 
-section#new_team div.new_unit div.new_unit_autocomplete div span{
+section#new_team div#new_unit_autocomplete div span{
     vertical-align: middle;
 }
 
-section#new_team div.new_unit div.new_unit_autocomplete div img{
+section#new_team div#new_unit_autocomplete div img{
     height: 1.5em;
     width: 1.5em;
     vertical-align: middle;

+ 19 - 9
public/css/ui.css

@@ -759,8 +759,8 @@ table.rune td.details table.table_details span.quality.quality_legend{background
  * Craft item panel
  */
 div.craft_panel{
-    width: 3em;
-    height: 3em;
+    width: 4em;
+    height: 4em;
     border: 0.1em solid #3c2929;
     border-radius: 0.5em;
     position: relative;
@@ -784,18 +784,18 @@ div.craft_panel.craft_quality_legend{ /* Legend */
 }
 div.craft_panel img.craft_base{
     position: absolute;
-    width: 3em;
-    height: 3em;
+    width: 4em;
+    height: 4em;
     margin: -0.5em;
     top: 0.1em;
     left: 0.5em;
 }
 div.craft_panel img.craft_icon{
     position: absolute;
-    width: 1em;
-    height: 1em;
-    top: 0.6em;
-    left: 1em;
+    width: 1.5em;
+    height: 1.5em;
+    top: 0.8em;
+    left: 1.25em;
 }
 div.craft_panel.craft_ancient_1 img.craft_base{filter: brightness(150%) contrast(90%) saturate(100%) drop-shadow(0 0 0.3em #ffffff) drop-shadow(0 0 0.1em #ffffff);}
 div.craft_panel.craft_quality_normal img.craft_base{filter: sepia(100%) saturate(500%) grayscale(100%);}
@@ -807,7 +807,7 @@ div.craft_panel span.craft_stat{
     display: block;
     width: 100%;
     text-align: center;
-    font-size: 60%;
+    font-size: 80%;
     text-shadow: 0 0 0.2em #000000, 0 0 0.2em #000000, 0 0 0.2em #000000, 0 0 0.2em #000000, 0 0 0.2em #000000;
     position: absolute;
     top: 2.7em;
@@ -821,3 +821,13 @@ div.craft_panel span.craft_stat span.craft_stat_value{
     font-weight: bold;
     margin: 0;
 }
+
+div.craft_panel span.craft_amount{
+    display: block;
+    position: absolute;
+    top: 0;
+    right: 0.2em;
+    text-align: right;
+    font-size: 90%;
+    font-weight: bold;
+    text-shadow: 0 0 0.2em #000000, 0 0 0.2em #000000, 0 0 0.2em #000000, 0 0 0.2em #000000, 0 0 0.2em #000000;

BIN
public/img/inventory/000009003.png