Bladeren bron

Stats page added.

Iñigo Valentin 6 jaren geleden
bovenliggende
commit
3c809c85ef

+ 11 - 0
application/Constant.php

@@ -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

@@ -180,6 +180,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();

+ 251 - 0
application/page/Stats_Page.php

@@ -0,0 +1,251 @@
+ <?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 = false;
+
+        /**
+         * @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->parse_filters();
+
+            // 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"];
+            }
+            
+            
+            $this->title = "Stats - SWDB";
+            $this->description = "Player statistics";
+            $this->canonical = URL::BASE . "stats/";
+        }
+
+        /**
+         * 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;
+        }
+    }
+?>

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

@@ -0,0 +1,70 @@
+<?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?>/runs/' method='get' id='filter_runeupgrade' class='filter'>
+    <table>
+        <tr>
+            <td class='filter'>
+<?php
+                $selected = array_fill(0, 12 + 1, "");
+                $selected[$filters["AREA_TYPE"]] = "selected";
+?>
+                <span>Region:</span>
+                <select name='area_type' id='filter_area'>
+                    <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>
+                    <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>
+            </td>
+            <td class='filter'>
+<?php
+                $selected = array_fill(0, 9002, "");
+                $selected[$filters["AREA"]] = "selected";
+?>
+                <span>Area:</span>
+                <select name='area_scenario'>
+                    <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>
+                </select>
+                <select name='area_cairos'>
+                    <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>
+                </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>

+ 593 - 0
application/view/stats.php

@@ -0,0 +1,593 @@
+<?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 populateDiagrams(){
+                diagramWin();
+                diagramDropType();
+                diagramDropRuneSlot();
+                diagramDropRuneStars();
+                diagramDropRuneQuality();
+            }
+
+            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 = ['#00ff00','#ff0000'];
+
+                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);
+                }
+            }
+
+            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 = ['#00ff00', '#ff0000', '#0000ff' , '#ffff00', '#ff00ff', '#00ffff'];
+
+                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);
+                }
+            }
+
+            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;
+                var lastend = 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()
+
+                // 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)
+            }
+
+            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;
+                var lastend = 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()
+
+                // 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)
+            }
+
+            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 * Math.PI / 180;
+                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()
+
+                // 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();'>
+<?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 -->
+        <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>
+                    <table class='legend'>
+                        <tr class='color_1'>
+                            <td>
+                                Victories:
+                            </td>
+                            <td>
+                                <?=$page->win_lose[0]?>
+                            </td>
+                            <td>
+                                (<?=number_format(($page->win_lose[0] * 100) / ($page->win_lose[0] + $page->win_lose[1]), 2, ".", ",")?>%)
+                            </td>
+                        </tr>
+                        <tr class='color_2'>
+                            <td>
+                                Defeats:
+                            </td>
+                            <td>
+                                <?=$page->win_lose[1]?>
+                            </td>
+                            <td>
+                                (<?=number_format(($page->win_lose[1] * 100) / ($page->win_lose[0] + $page->win_lose[1]), 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;
+?>
+                        <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);
+                        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);
+                        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);
+?>
+                        <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>

+ 39 - 0
public/css/stats.css

@@ -0,0 +1,39 @@
+div.diagram{
+    display: inline-block;
+    vertical-align: top;
+    text-align: center;
+    border: 0.2em solid #000000;
+}
+
+div.diagram h3{
+    
+}
+
+div.diagram table.legend{
+    list-style: none;
+    text-align: left;
+    font-weight: bold;
+}
+
+div.diagram table.legend tr.color_0{color: #ffffff;}
+div.diagram table.legend tr.color_1{color: #00ff00;}
+div.diagram table.legend tr.color_2{color: #ff0000;}
+div.diagram table.legend tr.color_3{color: #0000ff;}
+div.diagram table.legend tr.color_4{color: #ffff00;}
+div.diagram table.legend tr.color_5{color: #ff00ff;}
+div.diagram table.legend tr.color_6{color: #00ffff;}
+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.2em #000000;
+}
+
+div#drop_rune_stars img.star{
+    width: 1em;
+    height: 1em;
+    margin-right: -0.5em;
+}