Forráskód Böngészése

Merge branch 'Runs'

Inigo Valentin 6 éve
szülő
commit
9e61c88175

+ 66 - 0
application/Constant.php

@@ -61,6 +61,72 @@
         "GUILD" => 5,
     ];
 
+    $AREA_TYPE = [
+        'SCENARIO' => 1,
+        'CAIROS_DUNGEON' => 2,
+        'RIFT_DUNGEON' => 3,
+        'RIFT_RAID' => 4,
+        'DIMENSIONAL_HOLE' => 5,
+        'ARENA' => 6,
+        'GUILD_WAR' => 7,
+        'GUILD_SIEGE' => 8,
+        'TARTARUS_LABYRINTH' => 9,
+        'TRIAL_OF_ASCENSION' => 10,
+        'WORLD_BOSS' => 11,
+        'DIMENSIONAL_RIFT' => 12,
+    ];
+
+    $SCENARIO = [
+        'GAREN_FOREST' => 1,
+        'MT._SIZ' => 2,
+        'KABIR_RUINS' => 3,
+        'MT._WHITE_RAGON' => 4,
+        'TELAIN_FOREST' => 5,
+        'HYDENI_RUINS' => 6,
+        'TAMOR_DESERT' => 7,
+        'VROFAGUS_RUINS' => 8,
+        'FAIMON_VOLCANO' => 9,
+        'AIDEN_FOREST' => 10,
+        'FERUN_CASTLE' => 11,
+        'MT_RUNAR' => 12,
+        'CHIRUKA_REMAINS' => 13,
+    ];
+
+    $DUNGEON = [
+        'HALL_OF_DARK' => 1001,
+        'SANCTUARY_OF_DREAMING_FAIRIES' => 1101,
+        'ELLUNIA_REMAINS_FAIRY' => 1201,
+        'ELLUNIA_REMAINS_PIXIE' => 1202,
+        'HALL_OF_FIRE' => 2001,
+        'FOREST_OF_ROARING_BEASTS' => 2101,
+        'KARZHAN_REMAINS_WARBEAR' => 2201,
+        'KARZHAN_REMAINS_INUGAMI' => 2202,
+        'HALL_OF_WATER' => 3001,
+        'HALL_OF_WIND' => 4001,
+        'HALL_OF_MAGIC' => 5001,
+        'NECROPOLIS' => 6001,
+        'HALL_OF_LIGHT' => 7001,
+        'GIANTS_KEEP' => 8001,
+        'DRAGONS_LAIR' => 9001,
+
+    ];
+
+    $ELEMENTAL_RIFT_DUNGEON = [
+        'ICE_BEAST' => 1001,
+        'FIRE_BEAST' => 2001,
+        'WIND_BEAST' => 3001,
+        'LIGHT_BEAST' => 4001,
+        'DARK_BEAST' => 5001,
+    ];
+
+    $RAID_RIFT_DUNGEON = [
+        'LEVEL_1' => 1,
+        'LEVEL_2' => 2,
+        'LEVEL_3' => 3,
+        'LEVEL_4' => 4,
+        'LEVEL_5' => 5,
+    ];
+
     $BUILDING = [
         'SUMMONERS_TOWER' => 1,
         'SUMMONHENGE' => 2,

+ 4 - 0
application/Controller.php

@@ -57,6 +57,10 @@
                     require_once($path["page"] . "Guild_Page.php");
                     $page = new Guild_Page($db);
                 }
+                elseif (strtoupper($pars[0]) == "RUNS"){
+                    require_once($path["page"] . "Runs_Page.php");
+                    $page = new Runs_Page($db);
+                }
                 elseif (strtoupper($pars[0]) == "MONSTERS"){
                     if (count($pars) == 1){
                         require_once($path["page"] . "Monsters_Page.php");

+ 12 - 0
application/Filter.php

@@ -131,4 +131,16 @@
         "assigned" => 1,
         "group" => "SLOT"
     ];
+
+    $FILTER_RUN = [
+        "area_type" => 0,
+        "area" => 0,
+        "stage" => 0,
+        "difficulty" => 0,
+        "date_min" => null,
+        "date_max" => null,
+        "defeat" => false,
+        "helper" => false,
+        "number" => 20,
+    ];
 ?>

+ 136 - 0
application/bin/run-log.py

@@ -0,0 +1,136 @@
+#!/usr/bin/python3
+
+import sqlite3
+import json
+import sys
+
+def readData():
+    try:
+        print(sys.argv[1])
+        data = json.loads(sys.argv[1])
+        return data
+    except Exception as e:
+        print("Error parsng data: " + str(e))
+        raise
+
+"""
+Opens the database file and deletes from the user tables
+
+:param name: The path to the sqlite database.
+:returns: Connection to the database.
+:raises IntegrityError: The queryes couldn't bre executed.
+:raises IOError: The sqlite file couldn't be created.
+"""
+def openDatabase(name):
+    print('Configuring database...')
+    try:
+        db = sqlite3.connect(name)
+        return db
+    except IOError as e:
+        print("I/O Error connecting to database " + name + ": " + str(e))
+        raise
+
+
+"""
+Inserts a row into the database.
+
+:param db: Connection to the database.
+:param table: Name of the table to insert into.
+:param values: List of values to insert.
+:raises IntegrityError: The insert query was unsuccesfull.
+"""
+def insert(db, table, values):
+    cursor = db.cursor()
+    placeholders = ''
+    for x in range(0, len(values)):
+        placeholders = placeholders + '?, '
+    placeholders = placeholders[:len(placeholders) - 2]
+    query = 'INSERT INTO ' + table + ' VALUES (' + placeholders + ')'
+    try:
+        cursor.execute(query, values)
+    except sqlite3.IntegrityError as e:
+        print('Error Inserting into ' + table + ' with values (' + str(values) + '): ' + str(e))
+        raise
+    cursor.close;
+
+"""
+Parses run data (tables run, run_party, run_drop_rune, run_drop_runecraft,
+run_drop_item, run_drop_unit, run_drop_sd, run_drop_unit_pieces,
+run_drop_shapeshifting).
+
+:param db: Sqlite database connection.
+:param data: JSON data.
+"""
+def parseRun(db, data):
+    print("Parsing run...")
+    cursor = db.cursor()
+    uid = data["uid"]
+    dtime = data["dtime"]
+    # Check if run has already been inserted.
+    cursor.execute("SELECT count(id) AS c FROM run WHERE uid = ? AND dtime = ?;", (uid, dtime))
+    if (cursor.fetchone()[0] > 0):
+        print("    Run already in database. Stopping....")
+        return;
+    area = data["area"]
+    stage = data["stage"]
+    difficulty = data["difficulty"]
+    win = data["win"]
+    time = data["time"]
+    mana = data["mana"]
+    energy = data["energy"]
+    crystal = data["crystal"]
+    helper = data["helper"]
+    cursor.execute("SELECT max(id) + 1 AS id FROM run;")
+    id = cursor.fetchone()[0]
+    insert(db, "run", (uid, id, dtime, area, stage, difficulty, win, time, mana, energy, crystal, helper))
+    if data["shapeshifting"] > 0:
+        insert(db, "run_drop_shapeshifting", (id, data["shapeshifting"]))
+    if data["sd"] > 0:
+        insert(db, "run_drop_sd", (id, data["sd"]))
+    if data["sd"] > 0:
+        insert(db, "run_drop_unit", (id, data["unit"]))
+    for rune in data["rune"]:
+        rune_id = rune["id"]
+        rune_type = rune["type"]
+        slot = rune["slot"]
+        stars = rune["stars"]
+        ancient = rune["ancient"]
+        quality = rune["quality"]
+        value = rune["value"]
+        efficiency = rune["efficiency"]
+        main_stat = rune["main_stat"]
+        main_stat_value = rune["main_stat_value"]
+        innate_stat = rune["innate_stat"]
+        innate_stat_value = rune["innate_stat_value"]
+        substat_1 = rune["substat_1"]
+        substat_1_value = rune["substat_1_value"]
+        substat_2 = rune["substat_2"]
+        substat_2_value = rune["substat_2_value"]
+        substat_3 = rune["substat_3"]
+        substat_3_value = rune["substat_3_value"]
+        substat_4 = rune["substat_4"]
+        substat_4_value = rune["substat_4_value"]
+        insert(db, "run_drop_rune", (id, rune_id, rune_type, slot, stars, ancient, quality, value, efficiency, main_stat, main_stat_value, innate_stat, innate_stat_value, substat_1, substat_1_value, substat_2, substat_2_value, substat_3, substat_3_value, substat_4, substat_4_value))
+    for item in data["item"]:
+        item_id = item["id"]
+        quantity = item["quantity"]
+        insert(db, "run_drop_item", (id, item_id, quantity))
+    for pieces in data["unit_pieces"]:
+        pieces_id = pieces["id"]
+        quantity = pieces["quantity"]
+        insert(db, "run_drop_unit_pieces", (id, pieces_id, quantity))
+    for party in data["party"]:
+        unit_id = party["unit_id"]
+        unit_master_id = party["unit_master_id"]
+        insert(db, "run_party", (id, unit_master_id, unit_id))
+    db.commit()
+
+"""
+Begin script
+"""
+data = readData()
+# Path relative to the PHP script, not this one.
+db = openDatabase('../../../../application/sw.sqlite')
+parseRun(db, data)
+
+

+ 134 - 0
application/bin/save_run.py

@@ -0,0 +1,134 @@
+#!/usr/bin/python3
+
+import sqlite3
+import json
+
+def readData(file):
+    try:
+        data = json.loads(sys.argv[1])
+        return data
+    except Error as e:
+        print("Error parsng data: " + str(e))
+        raise
+
+"""
+Opens the database file and deletes from the user tables
+
+:param name: The path to the sqlite database.
+:returns: Connection to the database.
+:raises IntegrityError: The queryes couldn't bre executed.
+:raises IOError: The sqlite file couldn't be created.
+"""
+def openDatabase(name):
+    global OPT_CREATE_DB
+    print('Configuring database...')
+    try:
+        db = sqlite3.connect(name)
+        return db
+    except IOError as e:
+        print("I/O Error connecting to database " + name + ": " + str(e))
+        raise
+
+
+"""
+Inserts a row into the database.
+
+:param db: Connection to the database.
+:param table: Name of the table to insert into.
+:param values: List of values to insert.
+:raises IntegrityError: The insert query was unsuccesfull.
+"""
+def insert(db, table, values):
+    cursor = db.cursor()
+    placeholders = ''
+    for x in range(0, len(values)):
+        placeholders = placeholders + '?, '
+    placeholders = placeholders[:len(placeholders) - 2]
+    query = 'INSERT INTO ' + table + ' VALUES (' + placeholders + ')'
+    try:
+        cursor.execute(query, values)
+    except sqlite3.IntegrityError as e:
+        print('Error Inserting into ' + table + ' with values (' + str(values) + '): ' + str(e))
+        raise
+    cursor.close;
+
+"""
+Parses run data (tables run, run_party, run_drop_rune, run_drop_runecraft,
+run_drop_item, run_drop_unit, run_drop_sd, run_drop_unit_pieces,
+run_drop_shapeshifting).
+
+:param db: Sqlite database connection.
+:param data: JSON data.
+"""
+def parseUnits(db, data):
+    print("Parsing run...")
+    cursor = db.cursor()
+    uid = data["uid"]
+    dtime = data["dtime"]
+    # Check if run has already been inserted.
+    cursor.execute("SELECT count(id) AS c FROM run WHERE uid = ? AND dtime = ?;", (uid, dtime))
+    if (cur.fetchone()["c"] > 0):
+        print("    Run already in database. Stopping....")
+        return;
+    area = data["area"]
+    stage = data["stage"]
+    difficulty = data["difficulty"]
+    win = data["win"]
+    time = data["time"]
+    mana = data["mana"]
+    energy = data["energy"]
+    crystal = data["crystal"]
+    helper = data["helper"]
+    cursor.execute("SELECT max(id) + 1 AS id FROM run;")
+    id = cur.fetchone()["c"]
+    insert(db, "run", (uid, id, dtime, area, stage, difficulty, win, time, mana, energy, crystal, helper))
+    if data["sapeshifting"] > 0:
+        insert(db, "run_drop_sapeshifting", (id, data["sapeshifting"]))
+    if data["sd"] > 0:
+        insert(db, "run_drop_sd", (id, data["sd"]))
+    if data["sd"] > 0:
+        insert(db, "run_drop_unit", (id, data["unit"]))
+    for rune in data["rune"]:
+        rune_id = rune["id"]
+        rune_type = rune["type"]
+        slot = rune["slot"]
+        stars = rune["stars"]
+        ancient = rune["ancient"]
+        quality = rune["quality"]
+        value = rune["value"]
+        efficiency = rune["efficiency"]
+        main_stat = rune["main_stat"]
+        main_stat_value = rune["main_stat_value"]
+        innate_stat = rune["innate_stat"]
+        innate_stat_value = rune["innate_stat_value"]
+        substat_1 = rune["substat_1"]
+        substat_1_value = rune["substat_1_value"]
+        substat_2 = rune["substat_2"]
+        substat_2_value = rune["substat_2_value"]
+        substat_3 = rune["substat_3"]
+        substat_3_value = rune["substat_3_value"]
+        substat_4 = rune["substat_4"]
+        substat_4_value = rune["substat_4_value"]
+        insert(db, "run_drop_rune", (id, rune_id, rune_type, slot, stars, ancient, quality, value, efficiency, main_stat, main_stat_value, innate_stat, innate_stat_value, substat_1, substat_1_value, substat_2, substat_2_value, substat_3, substat_3_value, substat_4, substat_4_value))
+    for item in data["item"]:
+        item_id = item["id"]
+        quantity = item["quantity"]
+        insert(db, "run_drop_item", (id, item_id, quantity))
+    for pieces in data["unit_pieces"]:
+        pieces_id = pieces["id"]
+        quantity = pieces["quantity"]
+        insert(db, "run_drop_unit_pieces", (id, pieces_id, quantity))
+    for party in data["party"]:
+        unit_id = party["unit_id"]
+        unit_master_id = party["unit_master_id"]
+        insert(db, "run_party", (id, unit_id, unit_master_id))
+    db.commit()
+
+"""
+Begin script
+"""
+data = readData()
+db = openDatabase('../sw.sqlite')
+parseRun(db, data)
+
+

+ 5 - 0
application/entity/Inventory.php

@@ -21,6 +21,11 @@
          */
         public $id;
 
+        /**
+         * Item name.
+         */
+        public $name;
+
         /**
          * Item type name.
          */

+ 260 - 0
application/entity/K_Area.php

@@ -0,0 +1,260 @@
+<?php
+
+    require_once($path["entity"] . "Entity.php");
+
+
+    /**
+     * An area.
+     *
+     * Represents an object from the table 'k_area'.
+     */
+    class K_Area extends Entity{
+
+        /**
+         * Area identifier (it may not be unique!).
+         */
+        public $id;
+
+        /**
+         * Area type.
+         */
+        public $type;
+
+        /**
+         * Area name.
+         */
+        public $name;
+
+        /**
+         * Constructor.
+         *
+         * Searches the database and retrieves the information about the
+         * run, populating it and it's items.
+         *
+         * @param SQLite3 $db Connection to the database.
+         * @param int $id Area identifier.
+         * @param int $type Area type (required).
+         */
+        public function __construct($db, $id, $type){
+            global $path;
+            global $AREA_TYPE;
+            parent::__construct($db);
+            $this->id = $id;
+            $s = "
+                SELECT
+                  id,
+                  type,
+                  name
+                FROM k_area
+                WHERE id = '$id'
+                ORDER BY type = '$type' DESC;
+            ";
+            $q = $this->db->query($s);
+            $r = $q->fetchArray(SQLITE3_ASSOC);
+            if ($r){
+                $this->type = $r["type"];
+                $this->name = $r["name"];
+            }
+            elseif($this->id > 1000){
+                $this->type = $AREA_TYPE["CAIROS_DUNGEON"];
+                $this->name = "Hall of Heroes";
+            }
+        }
+
+        /**
+         * Gets the path to the area image. The image must be in the
+         * img/content/area/ directory, and its name must be the monster id,
+         * padded with '0' to 9 digites, and the extension must be '.png'.
+         *
+         * @return path to the image, or a path to a fixed unknown image if it
+         *         doesn't exist.
+         */
+        public function get_image($stage = null){
+            global $base_dir;
+            global $path;
+            global $AREA_TYPE;
+            global $DUNGEON;
+            if ($this->id > 10000 && file_exists($base_dir . "img/content/area/HOH.png")){
+                // Hall of Heroes
+                return $path["img"]["content"] . "area/HOH.png";
+            }
+            elseif (
+              (
+                $this->type == $AREA_TYPE["SCENARIO"] ||
+                $this->type == $AREA_TYPE["RIFT_DUNGEON"] ||
+                $this->type == $AREA_TYPE["DIMENSIONAL_HOLE"] ||
+                $this->type == $AREA_TYPE["RIFT_RAID"] ||
+                $this->type == $AREA_TYPE["ARENA"] ||
+                $this->type == $AREA_TYPE["GUILD_WAR"] ||
+                $this->type == $AREA_TYPE["GUILD_SIEGE"] ||
+                $this->type == $AREA_TYPE["TARTARUS_LABYRINTH"] ||
+                $this->type == $AREA_TYPE["TRIAL_OF_ASCENSION"] ||
+                $this->type == $AREA_TYPE["WORLD_BOSS"] ||
+                $this->type == $AREA_TYPE["DIMENSIONAL_RIFT"]
+              ) && file_exists($base_dir . "img/content/area/" . str_pad($this->id, 9, '0', STR_PAD_LEFT) . ".png")
+            ){
+                return $path["img"]["content"] . "area/" . str_pad($this->id, 9, '0', STR_PAD_LEFT) . ".png";
+            }
+            elseif ($this->type == $AREA_TYPE["CAIROS_DUNGEON"] && in_array($this->id, $DUNGEON)){
+                switch ($this->id){
+                    case $DUNGEON["GIANTS_KEEP"]:
+                        switch ($stage){
+                            case 1:
+                            case 6:
+                                // Light
+                                if (file_exists($base_dir . "img/content/unit/00060204.png")){
+                                    return $path["img"]["content"] . "unit/00060204.png";
+                                }
+                                break;
+                                break;
+                            case 2:
+                            case 7:
+                                // Dark
+                                if (file_exists($base_dir . "img/content/unit/00060204.png")){
+                                    return $path["img"]["content"] . "unit/00060204.png";
+                                }
+                                break;
+                            case 3:
+                            case 8:
+                                // Wind
+                                if (file_exists($base_dir . "img/content/unit/00060203.png")){
+                                    return $path["img"]["content"] . "unit/00060203.png";
+                                }
+                                break;
+                            case 4:
+                            case 9:
+                                // Fire
+                                if (file_exists($base_dir . "img/content/unit/00060202.png")){
+                                    return $path["img"]["content"] . "unit/00060202.png";
+                                }
+                                break;
+                            case 5:
+                            case 10:
+                                // Water
+                                if (file_exists($base_dir . "img/content/unit/00060201.png")){
+                                    return $path["img"]["content"] . "unit/00060201.png";
+                                }
+                                break;
+                        }
+                        break;
+                    case $DUNGEON["DRAGONS_LAIR"]:
+                        switch ($stage){
+                            case 1:
+                            case 6:
+                                // Wind
+                                if (file_exists($base_dir . "img/content/unit/00060303.png")){
+                                    return $path["img"]["content"] . "unit/00060303.png";
+                                }
+                                break;
+                                break;
+                            case 2:
+                            case 7:
+                                // Light
+                                if (file_exists($base_dir . "img/content/unit/00060304.png")){
+                                    return $path["img"]["content"] . "unit/00060304.png";
+                                }
+                                break;
+                            case 3:
+                            case 8:
+                                // Water
+                                if (file_exists($base_dir . "img/content/unit/00060301.png")){
+                                    return $path["img"]["content"] . "unit/00060301.png";
+                                }
+                                break;
+                            case 4:
+                            case 9:
+                                // Dark
+                                if (file_exists($base_dir . "img/content/unit/00060305.png")){
+                                    return $path["img"]["content"] . "unit/00060305.png";
+                                }
+                                break;
+                            case 5:
+                            case 10:
+                                // Fire
+                                if (file_exists($base_dir . "img/content/unit/00060302.png")){
+                                    return $path["img"]["content"] . "unit/00060302.png";
+                                }
+                                break;
+                        }
+                        break;
+                    case $DUNGEON["NECROPOLIS"]:
+                        switch ($stage){
+                            case 1:
+                            case 6:
+                                // Water
+                                if (file_exists($base_dir . "img/content/unit/000621301.png")){
+                                    return $path["img"]["content"] . "unit/000621301.png";
+                                }
+                                break;
+                                break;
+                            case 2:
+                            case 7:
+                                // Fire
+                                if (file_exists($base_dir . "img/content/unit/000621302.png")){
+                                    return $path["img"]["content"] . "unit/000621302.png";
+                                }
+                                break;
+                            case 3:
+                            case 8:
+                                // Wind
+                                if (file_exists($base_dir . "img/content/unit/000621303.png")){
+                                    return $path["img"]["content"] . "unit/000621303.png";
+                                }
+                                break;
+                            case 4:
+                            case 9:
+                                // Light
+                                if (file_exists($base_dir . "img/content/unit/000621304.png")){
+                                    return $path["img"]["content"] . "unit/000621304.png";
+                                }
+                                break;
+                            case 5:
+                            case 10:
+                                // Dark
+                                if (file_exists($base_dir . "img/content/unit/000621305.png")){
+                                    return $path["img"]["content"] . "unit/000621305.png";
+                                }
+                                break;
+                        }
+                        break;
+                    case $DUNGEON["HALL_OF_WATER"]:
+                        if (file_exists($base_dir . "img/content/unit/00060101.png")){
+                            return $path["img"]["content"] . "unit/00060101.png";
+                        }
+                        break;
+                    case $DUNGEON["HALL_OF_FIRE"]:
+                        if (file_exists($base_dir . "img/content/unit/00060102.png")){
+                            return $path["img"]["content"] . "unit/00060102.png";
+                        }
+                        break;
+                    case $DUNGEON["HALL_OF_WIND"]:
+                        if (file_exists($base_dir . "img/content/unit/00060103.png")){
+                            return $path["img"]["content"] . "unit/00060103.png";
+                        }
+                        break;
+                    case $DUNGEON["HALL_OF_LIGHT"]:
+                        if (file_exists($base_dir . "img/content/unit/00060104.png")){
+                            return $path["img"]["content"] . "unit/00060104.png";
+                        }
+                        break;
+                    case $DUNGEON["HALL_OF_DARK"]:
+                        if (file_exists($base_dir . "img/content/unit/00060105.png")){
+                            return $path["img"]["content"] . "unit/00060105.png";
+                        }
+                        break;
+                    case $DUNGEON["HALL_OF_MAGIC"]:
+                        if (file_exists($base_dir . "img/content/unit/00062004.png")){
+                            return $path["img"]["content"] . "unit/00062004.png";
+                        }
+                        break;
+                    default:
+                        if (file_exists($base_dir . "img/content/area/" . str_pad($this->id, 9, '0', STR_PAD_LEFT) . ".png")){
+                            return $path["img"]["content"] . "area/" . str_pad($this->id, 9, '0', STR_PAD_LEFT) . ".png";
+                        }
+                }
+            }
+            return $path["img"]["layout"] . "unknown.png";
+        }
+
+    }
+?>

+ 319 - 0
application/entity/Run.php

@@ -0,0 +1,319 @@
+<?php
+
+    require_once($path["entity"] . "Entity.php");
+    require_once($path["entity"] . "Unit.php");
+    require_once($path["entity"] . "K_Unit.php");
+    require_once($path["entity"] . "Rune.php");
+    require_once($path["entity"] . "Inventory.php");
+    require_once($path["entity"] . "K_Area.php");
+    require_once($path["entity"] . "K_Rune_Set.php");
+
+
+    /**
+     * A run.
+     *
+     * Represents an object from the table 'run'.
+     */
+    class Run extends Entity{
+
+        /**
+         * Player identifier.
+         */
+        public $uid;
+
+        /**
+         * Run identifier.
+         */
+        public $id;
+
+        /**
+         * Run start datestame.
+         */
+        public $dtime;
+
+        /**
+         * Area.
+         */
+        public $area;
+
+        /**
+         * Stage.
+         */
+        public $stage;
+
+        /**
+         * Difficulty (only scenario and dimension hole)
+         */
+        public $difficulty;
+
+        /**
+         * Win or lost run.
+         */
+        public $win;
+
+        /**
+         * Clear time.
+         */
+        public $time;
+
+        /**
+         * Reward mana.
+         */
+        public $mana;
+
+        /**
+         * Reward energy.
+         */
+        public $energy;
+
+        /**
+         * Reward crystal.
+         */
+        public $crystal;
+
+        /**
+         * Indicates if a frind/mentor helped.
+         */
+        public $helper;
+
+        /**
+         * Array of {@see Unit}s or {@see K_Unit}.
+         * If the unit is still owned, it will be a instance of Unit.
+         * If not, it will be a instance of K_Unit.
+         */
+        public $party = [];
+
+        /**
+         * List of {@see Inventory}, of dropped items.
+         */
+        public $item = [];
+
+        /**
+         * List of {@see K_Unit}s dropped.
+         */
+        public $unit = [];
+
+        /**
+         * List of {@see K_Unit}s whose secret dungeons were found on the run.
+         */
+        public $sd= [];
+
+        /**
+         * List of [{@see K_Unit}, quantity] of dropped summon pieces.
+         */
+        public $unit_piece = [];
+
+        /**
+         * Number of shapeshifting stones dropped;
+         */
+        public $shapeshifting = 0;
+
+        /**
+         * List of {@see Rune}s dropped.
+         */
+        public $rune= [];
+
+        /**
+         * Constructor.
+         *
+         * Searches the database and retrieves the information about the
+         * run, populating it and it's items.
+         *
+         * @param SQLite3 $db Connection to the database.
+         * @param int $id Run identifier.
+         */
+        public function __construct($db, $id){
+            global $path;
+            global $AREA_TYPE;
+            global $SCENARIO;
+            global $DUNGEON;
+            global $RAID_RIFT_DUNGEON;
+            global $ELEMENTAL_RIFT_DUNGEON;
+            parent::__construct($db);
+            $s = "
+                SELECT
+                  id,
+                  uid,
+                  dtime,
+                  area,
+                  stage,
+                  difficulty,
+                  win,
+                  time,
+                  mana,
+                  energy,
+                  crystal,
+                  helper
+                FROM run
+                WHERE id = $id;
+            ";
+            $q = $this->db->query($s);
+            $r = $q->fetchArray(SQLITE3_ASSOC);
+            $this->id = $r["id"];
+            $this->uid = $r["uid"];
+            $this->dtime = $r["dtime"];
+            $type = 0;
+            if (in_array($r["area"], $RAID_RIFT_DUNGEON) && $r["stage"] < 1){
+                $type = $AREA_TYPE["RIFT_RAID_DUNGEON"];
+            }
+            elseif (in_array($r["area"], $SCENARIO)){
+                $type = $AREA_TYPE["SCENARIO"];
+            }
+            elseif (in_array($r["area"], $ELEMENTAL_RIFT_DUNGEON) && $r["stage"] < 1){
+                $type = $AREA_TYPE["ELEMENTAL_RIFT_DUNGEON"];
+            }
+            elseif (in_array($r["area"], $DUNGEON)){
+                $type = $AREA_TYPE["CAIROS_DUNGEON"];
+            }
+            $this->area = new K_Area($this->db, $r["area"], $type);
+            $this->stage = $r["stage"];
+            $this->difficulty = $r["difficulty"];
+            $this->win = $r["win"];
+            $this->time = $r["time"];
+            $this->mana = $r["mana"];
+            $this->energy = $r["energy"];
+            $this->crystal = $r["crystal"];
+            $this->helper = $r["helper"];
+            $s = "
+                SELECT
+                  unit,
+                  k_unit,
+                  (SELECT count(id) FROM unit WHERE unit.unit = k_unit) AS owned
+                FROM run_party
+                WHERE run = $id;
+            ";
+            $q = $this->db->query($s);
+            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                if ($r["owned"] > 0){
+                    array_push($this->party, new Unit($this->db, $r["unit"], false));
+                }
+                else{
+                    array_push($this->party, new K_Unit($this->db, $r["k_unit"], false));
+                }
+            }
+            $s = "
+                SELECT
+                  item,
+                  quantity
+                FROM run_drop_item
+                WHERE run = $this->id;
+            ";
+            $q = $this->db->query($s);
+            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                $i = new Inventory($this->db, $r["item"]);
+                $i->amount =  $r["quantity"];
+                array_push($this->item, $i);
+            }
+            $s = "
+                SELECT unit
+                FROM run_drop_unit
+                WHERE run = $this->id;
+            ";
+            $q = $this->db->query($s);
+            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                array_push($this->unit, new K_Unit($this->db, $r["unit"]));
+            }
+            $s = "
+                SELECT unit
+                FROM run_drop_sd
+                WHERE run = $this->id;
+            ";
+            $q = $this->db->query($s);
+            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                array_push($this->sd, new K_Unit($this->db, $r["unit"]));
+            }
+            $s = "
+                SELECT
+                  unit,
+                  quantity
+                FROM run_drop_unit_pieces
+                WHERE run = $this->id;
+            ";
+            $q = $this->db->query($s);
+            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                $i = [
+                    "unit" => new K_Unit($this->db, $r["unit"]),
+                    "quantity" => $r["quantity"]
+                ];
+                array_push($this->unit_piece, $i);
+            }
+            $s = "
+                SELECT quantity
+                FROM run_drop_shapeshifting
+                WHERE run = $this->id;
+            ";
+            $q = $this->db->query($s);
+            $r = $q->fetchArray(SQLITE3_ASSOC);
+            if ($r){
+                $this->shapeshifting = $r["quantity"];
+            }
+            $s = "
+                SELECT
+                  id,
+                  type,
+                  slot,
+                  stars,
+                  ancient,
+                  quality,
+                  value,
+                  efficiency,
+                  main_stat,
+                  main_stat_value,
+                  innate_stat,
+                  innate_stat_value,
+                  substat_1,
+                  substat_1_value,
+                  substat_2,
+                  substat_2_value,
+                  substat_3,
+                  substat_3_value,
+                  substat_4,
+                  substat_4_value
+                FROM run_drop_rune
+                WHERE run = $this->id;
+            ";
+            $q = $this->db->query($s);
+            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                $rune = new Rune($this->db, $r["id"]);
+                if (!isset($rune->id)){
+                    $rune->id = $r["id"];
+                    $rune->assigned_to = null;
+                    $rune->type = new K_Rune_Set($this->db, $r["type"]);
+                    $rune->slot = $r["slot"];
+                    $rune->stars = $r["stars"];
+                    $rune->ancient = $r["ancient"];
+                    $rune->quality = $r["quality"];
+                    $rune->original_quality = $r["quality"];
+                    $rune->value = $r["value"];
+                    $rune->value = 0;
+                    $rune->efficiency = $r["efficiency"];
+                    // TODO: Calculate?
+                    $rune->max_efficiency = $r["efficiency"];
+                    $rune->main_stat = $r["main_stat"];
+                    $rune->main_stat_value = $r["main_stat_value"];
+                    $rune->innate_stat = $r["innate_stat"];
+                    $rune->innate_stat_value = $r["innate_stat_value"];
+                    $rune->substat_1 = $r["substat_1"];
+                    $rune->substat_1_value = $r["substat_1_value"];
+                    $rune->substat_1_enchant = 0;
+                    $rune->substat_1_grind = 0;
+                    $rune->substat_2 = $r["substat_2"];
+                    $rune->substat_2_value = $r["substat_2_value"];
+                    $rune->substat_2_enchant = 0;
+                    $rune->substat_2_grind = 0;
+                    $rune->substat_3 = $r["substat_3"];
+                    $rune->substat_3_value = $r["substat_3_value"];
+                    $rune->substat_3_enchant = 0;
+                    $rune->substat_3_grind = 0;
+                    $rune->substat_4 = $r["substat_4"];
+                    $rune->substat_4_value = $r["substat_4_value"];
+                    $rune->substat_4_enchant = 0;
+                    $rune->substat_4_grind = 0;
+                    $rune->refresh_names();
+                }
+                array_push($this->rune, $rune);
+            }
+        }
+
+    }
+?>

+ 15 - 8
application/entity/Rune.php

@@ -284,16 +284,23 @@
                 $this->substat_4_value = $r["substat_4_value"];
                 $this->substat_4_enchant = $r["substat_4_enchant"];
                 $this->substat_4_grind = $r["substat_4_grind"];
-                $this->main_stat_name = $this->stat_name($this->main_stat);
-                $this->innate_stat_name = $this->stat_name($this->innate_stat);
-                $this->substat_1_name = $this->stat_name($this->substat_1);
-                $this->substat_2_name = $this->stat_name($this->substat_2);
-                $this->substat_3_name = $this->stat_name($this->substat_3);
-                $this->substat_4_name = $this->stat_name($this->substat_4);
-                $this->quality_name = $this->quality_name($this->quality);
-                $this->original_quality_name = $this->quality_name($this->original_quality);
+                $this->refresh_names();
             }
         }
+        
+        /**
+         * Decodes some properties and assigns name variables.
+         */
+        public function refresh_names(){
+            $this->main_stat_name = $this->stat_name($this->main_stat);
+            $this->innate_stat_name = $this->stat_name($this->innate_stat);
+            $this->substat_1_name = $this->stat_name($this->substat_1);
+            $this->substat_2_name = $this->stat_name($this->substat_2);
+            $this->substat_3_name = $this->stat_name($this->substat_3);
+            $this->substat_4_name = $this->stat_name($this->substat_4);
+            $this->quality_name = $this->quality_name($this->quality);
+            $this->original_quality_name = $this->quality_name($this->original_quality);
+        }
 
         /**
          * Gets a stat name frm the maps.

+ 0 - 5
application/helper/html.php

@@ -23,26 +23,21 @@
         }
         $html = "";
         if ($k->awakens_from == "" && $k->awakens_to == ""){
-            error_log("COND1");
             // No to, no from, silver monster.
             $star_class ="star_silver";
         }
         elseif ($k->awakens_from != ""){
-            error_log("COND2");
             // Already awakened
             if ($k->base_stars - $k->natural_stars == 1){
-                error_log("COND2.1");
                 // Normal awaken.
                 $star_class ="star_purple";
             }
             else{
-                error_log("COND2.2");
                 // Second awaken.
                 $star_class ="star_red";
             }
         }
         elseif ($k->awakens_to != ""){
-            error_log("COND3");
             // Awakeable.
             $star_class ="star_gold";
         }

+ 0 - 2
application/page/Report_Runecraft_Page.php

@@ -42,8 +42,6 @@
         public function __construct($db){
             global $path;
             global $root;
-            global $a;
-            error_log("VAR a: " . $a);
             parent::__construct($db);
             $this->view = $path["view"] . "report_runecraft.php";
             $this->parse_filters();

+ 127 - 0
application/page/Runs_Page.php

@@ -0,0 +1,127 @@
+ <?php
+
+    require_once($path["page"] . "Page.php");
+    require_once($path["entity"] . "Run.php");
+
+    /**
+     * Run list page.
+     */
+    class Runs_Page extends Page{
+
+        /**
+         * Array of @{see Run}s.
+         */
+        public $runs = [];
+
+        /**
+         * Filters the page can handle.
+         */
+        public $filters;
+
+        /**
+         * Constructor.
+         *
+         * Retrieves the data and initializes the variables.
+         *
+         * @param SQLite3 $db Connection to the database.
+         */
+        public function __construct($db){
+            global $path;
+            global $root;
+            parent::__construct($db);
+            $this->view = $path["view"] . "runs.php";
+            $this->parse_filters();
+            $s = $this->build_query();
+            $q = $this->db->query($s);
+            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                array_push($this->runs, new Run($db, $r["id"]));
+            }
+            $this->title = "Runs - SWDB";
+            $this->description = "Run logs";
+            $this->canonical = $root . "runs/";
+        }
+
+        /**
+         * Parses the request looking for the selected filters, validates them
+         * and adds them to the $filters array.
+         */
+        private function parse_filters(){
+            global $FILTER_RUN;
+            global $DIFFICULTY;
+            $this->filters = $FILTER_RUN;
+            
+            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["NORMAL"] && intval($_GET["difficulty"]) <= $DIFFICULTY["HELL"]){
+                $this->filters["difficulty"] = $_GET["difficulty"];
+            }
+            if (isset($_GET["date_min"]) && strlen($_GET["date_min"]) > 0){
+                $this->filters["date_min"] = DateTime::createFromFormat('Y-m-d', $_GET["date_min"])->format('Y-m-d');
+            }
+            if (isset($_GET["date_max"]) && strlen($_GET["date_max"]) > 0){
+                $this->filters["date_max"] = DateTime::createFromFormat('Y-m-d', $_GET["date_max"])->format('Y-m-d');
+            }
+            if (isset($_GET["defeat"]) && $_GET["defeat"] == "on"){
+                $this->filters["defeat"] = true;
+            }
+            else{
+                $this->filters["helper"] = false;
+            }
+            if (isset($_GET["helper"]) && $_GET["helper"] == "on"){
+                $this->filters["helper"] = true;
+            }
+            else{
+                $this->filters["helper"] = false;
+            }
+            if (isset($_GET["number"]) && intval($_GET["number"]) > 0){
+                $this->filters["number"] = $_GET["number"];
+            }
+            return;
+        }
+
+        /**
+         * Builds the query to the run table using the selected or default
+         * filters.
+         * 
+         * @return The query to be executed.
+         */
+        private function build_query(){
+            $s = "
+                SELECT id
+                FROM run
+                WHERE 1 = 1
+             ";
+            if ($this->filters["area"] > 0){
+                $s = $s . " AND area =  " . $this->filters["area"] . " ";
+            }
+            if ($this->filters["stage"] > 0){
+                $s = $s . " AND stage =  " . $this->filters["stage"] . " ";
+            }
+            if ($this->filters["difficulty"] > 0){
+                $s = $s . " AND difficulty =  " . $this->filters["difficulty"] . " ";
+            }
+            if (!$this->filters["defeat"]){
+                $s = $s . " AND win = 1 ";
+            }
+            if (!$this->filters["helper"]){
+                $s = $s . " AND helper = 0 ";
+            }
+            if ($this->filters["date_min"] != null){
+                $s = $s . " AND date(dtime) >= date('" . $this->filters["date_min"] . "') ";
+            }
+            if ($this->filters["date_max"] != null){
+                $s = $s . " AND date(dtime) >= date('" . $this->filters["date_max"] . "') ";
+            }
+            $s = $s . " ORDER BY dtime DESC ";
+            $s = $s . " LIMIT " . $this->filters["number"] . ";";
+            return $s;
+        }
+    }
+?>

+ 116 - 0
application/view/inc/filter_runs.php

@@ -0,0 +1,116 @@
+<?php
+    global $AREA_TYPE;
+?>
+<form action='runs' method='get' id='filter_runeupgrade' class='filter'>
+    <table>
+        <tr>
+            <td class='filter filter_title'>
+                <span>
+                    Area
+                </span>
+            </td>
+            <td class='filter'>
+<?php
+                $selected = [];
+                $selected[$filters["area_type"]] = "selected";
+?>
+                <span>Region:</span>
+                <select name='area_type' id='filter_area'>
+                    <option value='0'>All</option>
+                    <option value='<?=$AREA_TYPE["SCENARIO"]?>' <?=$selected[$AREA_TYPE["SCENARIO"]]?>>Scenario</option>
+                    <option value='<?=$AREA_TYPE["CAIROS_DUNGEON"]?>' <?=$selected[$AREA_TYPE["CAIROS_DUNGEON"]]?>>Cairos Dungeon</option>
+                    <option value='<?=$AREA_TYPE["RIFT_DUNGEON"]?>' <?=$selected[$AREA_TYPE["RIFT_DUNGEON"]]?>>Rift Beast</option>
+                    <option value='<?=$AREA_TYPE["RIFT_RAID"]?>' <?=$selected[$AREA_TYPE["RIFT_RAID"]]?>>Rift Raid</option>
+                    <option value='<?=$AREA_TYPE["DIMENSIONAL_HOLE"]?>' <?=$selected[$AREA_TYPE["DIMENSIONAL_HOLE"]]?>>Dimensional Hole</option>
+                    <option value='<?=$AREA_TYPE["ARENA"]?>' <?=$selected[$AREA_TYPE["ARENA"]]?>>Arena</option>
+                    <option value='<?=$AREA_TYPE["GUILD_WAR"]?>' <?=$selected[$AREA_TYPE["GUILD_WAR"]]?>>Guild War</option>
+                    <option value='<?=$AREA_TYPE["GUILD_SIEGE"]?>' <?=$selected[$AREA_TYPE["GUILD_SIEGE"]]?>>Guild Siege</option>
+                    <option value='<?=$AREA_TYPE["TARTARUS_LABYRINTH"]?>' <?=$selected[$AREA_TYPE["TARTARUS_LABYRINTH"]]?>>Tartarus Labyrinth</option>
+                    <option value='<?=$AREA_TYPE["TRIAL_OF_ASCENSION"]?>' <?=$selected[$AREA_TYPE["TRIAL_OF_ASCENSION"]]?>>Trial of Ascension</option>
+                    <option value='<?=$AREA_TYPE["WORLD_BOSS"]?>' <?=$selected[$AREA_TYPE["WORLD_BOSS"]]?>>World Boss</option>
+                    <option value='<?=$AREA_TYPE["DIMENSIONAL_RIFT"]?>' <?=$selected[$AREA_TYPE["DIMENSIONAL_RIFT"]]?>>Dimensional Rift</option>
+                </select>
+            </td>
+            <td class='filter'>
+<?php
+                $selected = [];
+                $selected[$filters["area00"]] = "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>
+        <tr>
+            <td class='filter filter_title filter_separator' rowspan='4'>
+                <span>
+                    Extra
+                </span>
+            </td>
+            <td class='filter filter_separator' colspan='2'>
+                <span>Date range:</span>
+                <input type='date' name='date_min' id='date_min' value='<?=$filters["date_min"]?>'/>
+                -
+                <input type='date' name='date_max' id='date_max' value='<?=$filters["date_max"]?>'/>
+            </td>
+        </tr>
+        <tr>
+            <td class='filter' colspan='2'>
+<?php
+                $checked = "";
+                if ($filters["defeat"]){
+                    $checked = "checked";
+                }
+?>
+                <input type='checkbox' name='defeat' <?=$checked?>/>
+                Show defeats
+            </td>
+        </tr>
+        <tr>
+            <td class='filter' colspan='2'>
+<?php
+                $checked = "";
+                if ($filters["helper"]){
+                    $checked = "checked";
+                }
+?>
+                <input type='checkbox' name='helper' <?=$checked?>/>
+                Show runs with friends/mentors
+            </td>
+        </tr>
+        <tr>
+            <td class='filter' colspan='2'>
+                <input type='number' name='number' id='number' min='1' value='<?=$filters["number"]?>'/> runs per page
+            </td>
+        </tr>
+    </table>
+    <div id='filter_apply'>
+        <input type='submit' value='Apply'/>
+    </div>
+</form>

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

@@ -27,6 +27,9 @@
         <a href='/guild'>
             Guild
         </a>
+        <a href='/runs'>
+            Runs
+        </a>
         <a href='/report'>
             Reports
         </a>

+ 249 - 0
application/view/runs.php

@@ -0,0 +1,249 @@
+<?php
+    global $AREA_TYPE;
+    global $SCENARIO;
+?>
+<!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='<?=$path["css"]?>ui.css'/>
+        <link rel='stylesheet' type='text/css' href='<?=$path["css"]?>runs.css'/>
+        <link rel='stylesheet' type='text/css' href='<?=$path["css"]?>filters.css'/>
+        <!-- Script files -->
+        <script src="<?=$path["js"]?>ui.js"></script>
+        <!-- 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'/>
+    </head>
+    <body>
+<?php
+        include __DIR__ . "/inc/header.php";
+?>
+            <section class='filters'>
+                <h2>
+                    Run filters
+                </h2>
+<?php
+                $filters = $page->filters;
+                //TODO
+                include __DIR__ . "/inc/filter_runs.php";
+?>
+
+            </section> <!-- #filters -->
+            <section class='content'>
+                <article>
+                    <table id='runs'>
+                        <tr>
+                            <th>
+                                Area
+                            </th>
+                            <th>
+                                Date
+                            </th>
+                            <th>
+                                Team
+                            </th>
+                            <th>
+                                Time
+                            </th>
+                            <th>
+                                Reward
+                            </th>
+                        </tr>
+<?php
+                        foreach ($page->runs as $run){
+?>
+                            <tr>
+                                <td class='area'>
+                                    <div class='area'>
+                                        <img title='<?=$run->area->name?>' class='area' src='<?=$run->area->get_image($run->stage)?>'/>
+<?php
+                                        if ($run->stage > 0){
+?>
+                                            <span class='stage'><?=$run->stage?></span>
+<?php
+                                        }
+                                        // TODO: Labyrinth difficulty
+                                        if (in_array($run->area->id, $SCENARIO)){
+?>
+                                            <span class='difficulty'>
+<?php
+                                                for ($i = 0; $i < $run->difficulty; $i ++){
+?>
+                                                    <img class='difficulty' src='<?=$path["img"]["layout"]?>icon/star.png'/>
+<?php
+                                                }
+?>
+                                            </span>
+<?php
+                                        }
+?>
+                                    </div>
+                                </td>
+                                <td class='date'>
+<?php
+                                    $date = date_create($run->dtime);
+?>
+                                    <?=date_format($date, "Y/m/d H:i:s")?>
+                                </td>
+                                <td class='team'>
+<?php
+                                    if ($run->helper == 1){
+?>
+                                        <div class='monster_panel helper'>
+                                            <img class='monster' title='Friend/Menstor' src='<?=$path["img"]["layout"]?>currency/socialpoint.png'/>
+                                        </div>
+<?php
+                                    }
+                                    foreach ($run->party as $party){
+                                        if ($party instanceof K_Unit){
+                                            $disabled = "disabled";
+                                        }
+                                        else{
+                                            $disabled = "";
+                                        }
+                                        if ($disabled == ""){
+?>
+                                            <a target='_blank' href='/monster/<?=$party->id?>'>
+<?php
+                                        }
+?>
+                                        <div class='monster_panel <?=$disabled?>'>
+                                            <?=html_unit_panel($party)?>
+                                        </div>
+<?php
+                                        if ($disabled == ""){
+?>
+                                            </a>
+<?php
+                                        }
+                                    }
+?>
+                                </td>
+                                <td class='time'>
+<?php
+                                    if ($run->win == 1){
+                                        $s = floor($run->time / 1000);
+                                        $m = floor($s / 60);
+                                        $s = floor($s % 60);
+                                        $ms = ($run-> time - (1000 * 60 * $m) - (1000 * 60 * $s) / 100);
+                                        $s = str_pad($s, 2, '0', STR_PAD_LEFT);
+                                        $ms = str_pad($ms, 2, '0', STR_PAD_LEFT);
+?>
+                                        <?=$m?>:<?=$s?><span class='ms'>.<?=$ms?></span>
+<?php
+                                    }
+                                    else{
+?>
+                                        <span class='defeated'>Defeated</span>
+<?php
+                                    }
+?>
+                                </td>
+                                <td class='reward'>
+<?php
+                                    if ($run->energy > 0){
+?>
+                                        <div class='item'>
+                                            <img class='item' title='Energy' src='<?=$path["img"]["layout"]?>currency/energy.png'/>
+                                            <span class='item'><?=$run->energy?></span>
+                                        </div>
+<?php
+                                    }
+                                    if ($run->mana > 0){
+?>
+                                        <div class='item'>
+                                            <img class='item' title='Mana' src='<?=$path["img"]["layout"]?>currency/mana.png'/>
+                                            <span class='item'><?=$run->mana?></span>
+                                        </div>
+<?php
+                                    }
+                                    if ($run->crystal > 0){
+?>
+                                        <div class='item'>
+                                            <img class='item' title='Crystal' src='<?=$path["img"]["layout"]?>currency/crystal.png'/>
+                                            <span class='item'><?=$run->crystal?></span>
+                                        </div>
+<?php
+                                    }
+                                    foreach ($run->unit as $unit){
+?>
+                                        <div class='item'>
+                                            <img class='item' title='<?=$unit->title?>' src='<?=$unit->get_image()?>'/>
+                                        </div>
+<?php
+                                    }
+                                    foreach ($run->item as $item){
+?>
+                                        <div class='item'>
+                                            <img class='item' title='<?=$item->name?>' src='<?=$item->get_image()?>'/>
+                                            <span class='item'><?=$item->amount?></span>
+                                        </div>
+<?php
+                                    }
+                                    foreach ($run->sd as $sd){
+?>
+                                        <div class='item'>
+                                            <img class='item' title='<?=$sd->unit->title?>' src='<?=$sd->unit->get_image()?>'/>
+                                            <img class='item' title=' ' src='<?=$path["img"]["layout"]?>misc/mask-sd.png'/>
+                                        </div>
+<?php
+                                    }
+                                    foreach ($run->unit_piece as $piece){
+?>
+                                        <div class='item'>
+                                            <img class='item' title='<?=$piece->unit->title?>' src='<?=$piece->unit->get_image()?>'/>
+                                            <img class='item' title=' ' src='<?=$path["img"]["layout"]?>misc/mask-piece.png'/>
+                                            <span class='item'><?=$piece->quantity?></span>
+                                        </div>
+<?php
+                                    }
+                                    if ($run->shapeshifting > 0){
+?>
+                                        <div class='item'>
+                                            <img class='item' title='Shapeshifting Stones' src='<?=$path["img"]["layout"]?>currency/costumestone.png'/>
+                                            <span class='item'><?=$run->shapeshifting?></span>
+                                        </div>
+<?php
+                                    }
+                                    foreach ($run->rune as $rune){
+?>
+                                        <div class='item item_rune'>
+                                            <img class='item' title='Rune' src='<?=$path["img"]["layout"]?>rune/base.png'/>
+                                            <?=html_rune_table($rune)?>
+                                        </div>
+<?php
+                                    }
+?>
+                                </td>
+                            </tr>
+<?php
+                        }
+?>
+                    </table>
+                </article>
+            </section>
+<?php
+        include __DIR__ . "/inc/footer.php";
+?>
+    </body>
+</html>

+ 261 - 0
data.sql

@@ -40,6 +40,61 @@ CREATE TABLE IF NOT EXISTS k_rune_craft_type(id INT, name TEXT, gem INT, inmemor
 
 CREATE TABLE IF NOT EXISTS k_rune_craft_range(gem INT, ancient INT, quality INT, stat INT, min INT, max INT);
 
+CREATE TABLE IF NOT EXISTS k_area_type(id, name TEXT, effect INT);
+
+CREATE TABLE IF NOT EXISTS k_area (id INT, type INT, name TEXT);
+
+INSERT INTO k_area_type VALUES (1, 'Scenario', null);
+INSERT INTO k_area_type VALUES (2, 'Cairos Dungeons', 2);
+INSERT INTO k_area_type VALUES (3, 'Rift Elemental Dungeon', 2);
+INSERT INTO k_area_type VALUES (4, 'Rift Raid', null);
+INSERT INTO k_area_type VALUES (5, 'Dimensional Hole', 2);
+INSERT INTO k_area_type VALUES (6, 'Arena', 3);
+INSERT INTO k_area_type VALUES (7, 'Guild War', 5);
+INSERT INTO k_area_type VALUES (8, 'Guild Siege', 5);
+INSERT INTO k_area_type VALUES (9, 'Tartarus Labyrinth', 5);
+INSERT INTO k_area_type VALUES (10, 'Trial of Ascension', null);
+INSERT INTO k_area_type VALUES (11, 'World Boss', null);
+INSERT INTO k_area_type VALUES (12, 'Dimensional Rift', null);
+
+INSERT INTO k_area VALUES (1, 1, 'Garen Forest');
+INSERT INTO k_area VALUES (2, 1, 'Mt. Siz');
+INSERT INTO k_area VALUES (3, 1, 'Kabir Ruins');
+INSERT INTO k_area VALUES (4, 1, 'Mt. White Ragon');
+INSERT INTO k_area VALUES (5, 1, 'Telain Forest');
+INSERT INTO k_area VALUES (6, 1, 'Hydeni Ruins');
+INSERT INTO k_area VALUES (7, 1, 'Tamor Desert');
+INSERT INTO k_area VALUES (8, 1, 'Vrofagus Ruins');
+INSERT INTO k_area VALUES (9, 1, 'Faimon Volcano');
+INSERT INTO k_area VALUES (10, 1, 'Aiden Forest');
+INSERT INTO k_area VALUES (11, 1, 'Ferun Castle');
+INSERT INTO k_area VALUES (12, 1, 'Mt Runar');
+INSERT INTO k_area VALUES (13, 1, 'Chiruka Remains');
+INSERT INTO k_area VALUES (1001, 2, 'Hall of Dark');
+INSERT INTO k_area VALUES (1101, 5, 'Sanctuary of Dreaming Fairies');
+INSERT INTO k_area VALUES (1201, 5, 'Ellunia Remains (Fairy)');
+INSERT INTO k_area VALUES (1202, 5, 'Ellunia Remains (Pixie)');
+INSERT INTO k_area VALUES (2001, 2, 'Hall of Fire');
+INSERT INTO k_area VALUES (2101, 5, 'Forest of Roaring Beasts');
+INSERT INTO k_area VALUES (2201, 5, 'Karzhan Remains (Warbear)');
+INSERT INTO k_area VALUES (2202, 5, 'Karzhan Remains (Inugami)');
+INSERT INTO k_area VALUES (3001, 2, 'Hall of Water');
+INSERT INTO k_area VALUES (4001, 2, 'Hall of Wind');
+INSERT INTO k_area VALUES (5001, 2, 'Hall of Magic');
+INSERT INTO k_area VALUES (6001, 2, 'Necropolis');
+INSERT INTO k_area VALUES (7001, 2, 'Hall of Light');
+INSERT INTO k_area VALUES (8001, 2, 'Giant''s Keep');
+INSERT INTO k_area VALUES (9001, 2, 'Dragon''s Lair');
+INSERT INTO k_area VALUES (1001, 3, 'Rift Dungeon - Ice Beast');
+INSERT INTO k_area VALUES (2001, 3, 'Rift Dungeon - Fire Beast');
+INSERT INTO k_area VALUES (3001, 3, 'Rift Dungeon - Wind Beast');
+INSERT INTO k_area VALUES (4001, 3, 'Rift Dungeon - Light Beast');
+INSERT INTO k_area VALUES (5001, 3, 'Rift Dungeon - Dark Beast');
+INSERT INTO k_area VALUES (1, 4, 'Rift of Worlds - level 1');
+INSERT INTO k_area VALUES (2, 4, 'Rift of Worlds - level 2');
+INSERT INTO k_area VALUES (3, 4, 'Rift of Worlds - level 3');
+INSERT INTO k_area VALUES (4, 4, 'Rift of Worlds - level 4');
+INSERT INTO k_area VALUES (5, 4, 'Rift of Worlds - level 5');
 
 INSERT INTO k_difficulty VALUES (0, 'Easy');
 INSERT INTO k_difficulty VALUES (1, 'Normal');
@@ -328,6 +383,209 @@ INSERT INTO k_decoration VALUES (5, 1, null, null, 'Mana Fountain', '', 10);
 INSERT INTO k_decoration VALUES (10, 1, null, null, 'Sanctum of Energy', '', 10);
 INSERT INTO k_decoration VALUES (35, 1, null, null, 'Fairy Tree', '', 10);
 
+
+INSERT INTO k_decoration_level VALUES (4, 1, 2, 100);
+INSERT INTO k_decoration_level VALUES (4, 2, 4, 280);
+INSERT INTO k_decoration_level VALUES (4, 3, 6, 460);
+INSERT INTO k_decoration_level VALUES (4, 4, 8, 640);
+INSERT INTO k_decoration_level VALUES (4, 5, 10, 820);
+INSERT INTO k_decoration_level VALUES (4, 6, 12, 1000);
+INSERT INTO k_decoration_level VALUES (4, 7, 14, 1180);
+INSERT INTO k_decoration_level VALUES (4, 8, 16, 1360);
+INSERT INTO k_decoration_level VALUES (4, 9, 18, 1540);
+INSERT INTO k_decoration_level VALUES (4, 10, 20, 1720);
+INSERT INTO k_decoration_level VALUES (15, 1, 3, 120);
+INSERT INTO k_decoration_level VALUES (15, 2, 5, 240);
+INSERT INTO k_decoration_level VALUES (15, 3, 7, 360);
+INSERT INTO k_decoration_level VALUES (15, 4, 9, 480);
+INSERT INTO k_decoration_level VALUES (15, 5, 11, 600);
+INSERT INTO k_decoration_level VALUES (15, 6, 13, 720);
+INSERT INTO k_decoration_level VALUES (15, 7, 15, 840);
+INSERT INTO k_decoration_level VALUES (15, 8, 17, 960);
+INSERT INTO k_decoration_level VALUES (15, 9, 19, 1080);
+INSERT INTO k_decoration_level VALUES (15, 10, 21, 1200);
+INSERT INTO k_decoration_level VALUES (16, 1, 3, 120);
+INSERT INTO k_decoration_level VALUES (16, 2, 5, 240);
+INSERT INTO k_decoration_level VALUES (16, 3, 7, 360);
+INSERT INTO k_decoration_level VALUES (16, 4, 9, 480);
+INSERT INTO k_decoration_level VALUES (16, 5, 11, 600);
+INSERT INTO k_decoration_level VALUES (16, 6, 13, 720);
+INSERT INTO k_decoration_level VALUES (16, 7, 15, 840);
+INSERT INTO k_decoration_level VALUES (16, 8, 17, 960);
+INSERT INTO k_decoration_level VALUES (16, 9, 19, 1080);
+INSERT INTO k_decoration_level VALUES (16, 10, 21, 1200);
+INSERT INTO k_decoration_level VALUES (17, 1, 3, 120);
+INSERT INTO k_decoration_level VALUES (17, 2, 5, 240);
+INSERT INTO k_decoration_level VALUES (17, 3, 7, 360);
+INSERT INTO k_decoration_level VALUES (17, 4, 9, 480);
+INSERT INTO k_decoration_level VALUES (17, 5, 11, 600);
+INSERT INTO k_decoration_level VALUES (17, 6, 13, 720);
+INSERT INTO k_decoration_level VALUES (17, 7, 15, 840);
+INSERT INTO k_decoration_level VALUES (17, 8, 17, 960);
+INSERT INTO k_decoration_level VALUES (17, 9, 19, 1080);
+INSERT INTO k_decoration_level VALUES (17, 10, 21, 1200);
+INSERT INTO k_decoration_level VALUES (18, 1, 3, 120);
+INSERT INTO k_decoration_level VALUES (18, 2, 5, 240);
+INSERT INTO k_decoration_level VALUES (18, 3, 7, 360);
+INSERT INTO k_decoration_level VALUES (18, 4, 9, 480);
+INSERT INTO k_decoration_level VALUES (18, 5, 11, 600);
+INSERT INTO k_decoration_level VALUES (18, 6, 13, 720);
+INSERT INTO k_decoration_level VALUES (18, 7, 15, 840);
+INSERT INTO k_decoration_level VALUES (18, 8, 17, 960);
+INSERT INTO k_decoration_level VALUES (18, 9, 19, 1080);
+INSERT INTO k_decoration_level VALUES (18, 10, 21, 1200);
+INSERT INTO k_decoration_level VALUES (19, 1, 3, 120);
+INSERT INTO k_decoration_level VALUES (19, 2, 5, 240);
+INSERT INTO k_decoration_level VALUES (19, 3, 7, 360);
+INSERT INTO k_decoration_level VALUES (19, 4, 9, 480);
+INSERT INTO k_decoration_level VALUES (19, 5, 11, 600);
+INSERT INTO k_decoration_level VALUES (19, 6, 13, 720);
+INSERT INTO k_decoration_level VALUES (19, 7, 15, 840);
+INSERT INTO k_decoration_level VALUES (19, 8, 17, 960);
+INSERT INTO k_decoration_level VALUES (19, 9, 19, 1080);
+INSERT INTO k_decoration_level VALUES (19, 10, 21, 1200);
+INSERT INTO k_decoration_level VALUES (31, 1, 2, 120);
+INSERT INTO k_decoration_level VALUES (31, 2, 5, 240);
+INSERT INTO k_decoration_level VALUES (31, 3, 7, 360);
+INSERT INTO k_decoration_level VALUES (31, 4, 10, 480);
+INSERT INTO k_decoration_level VALUES (31, 5, 12, 600);
+INSERT INTO k_decoration_level VALUES (31, 6, 15, 720);
+INSERT INTO k_decoration_level VALUES (31, 7, 17, 840);
+INSERT INTO k_decoration_level VALUES (31, 8, 20, 960);
+INSERT INTO k_decoration_level VALUES (31, 9, 22, 1080);
+INSERT INTO k_decoration_level VALUES (31, 10, 25, 1200);
+INSERT INTO k_decoration_level VALUES (6, 1, 2, 240);
+INSERT INTO k_decoration_level VALUES (6, 2, 3, 440);
+INSERT INTO k_decoration_level VALUES (6, 3, 5, 640);
+INSERT INTO k_decoration_level VALUES (6, 4, 6, 840);
+INSERT INTO k_decoration_level VALUES (6, 5, 8, 1040);
+INSERT INTO k_decoration_level VALUES (6, 6, 9, 1240);
+INSERT INTO k_decoration_level VALUES (6, 7, 11, 1440);
+INSERT INTO k_decoration_level VALUES (6, 8, 12, 1640);
+INSERT INTO k_decoration_level VALUES (6, 9, 14, 1840);
+INSERT INTO k_decoration_level VALUES (6, 10, 15, 2040);
+INSERT INTO k_decoration_level VALUES (8, 1, 2, 200);
+INSERT INTO k_decoration_level VALUES (8, 2, 4, 400);
+INSERT INTO k_decoration_level VALUES (8, 3, 6, 600);
+INSERT INTO k_decoration_level VALUES (8, 4, 8, 800);
+INSERT INTO k_decoration_level VALUES (8, 5, 10, 1000);
+INSERT INTO k_decoration_level VALUES (8, 6, 12, 1200);
+INSERT INTO k_decoration_level VALUES (8, 7, 14, 1400);
+INSERT INTO k_decoration_level VALUES (8, 8, 16, 1600);
+INSERT INTO k_decoration_level VALUES (8, 9, 18, 1800);
+INSERT INTO k_decoration_level VALUES (8, 10, 20, 2000);
+INSERT INTO k_decoration_level VALUES (36, 1, 2, 280);
+INSERT INTO k_decoration_level VALUES (36, 2, 4, 460);
+INSERT INTO k_decoration_level VALUES (36, 3, 6, 800);
+INSERT INTO k_decoration_level VALUES (36, 4, 8, 1250);
+INSERT INTO k_decoration_level VALUES (36, 5, 10, 1810);
+INSERT INTO k_decoration_level VALUES (36, 6, 12, 2320);
+INSERT INTO k_decoration_level VALUES (36, 7, 14, 2910);
+INSERT INTO k_decoration_level VALUES (36, 8, 16, 3590);
+INSERT INTO k_decoration_level VALUES (36, 9, 18, 4350);
+INSERT INTO k_decoration_level VALUES (36, 10, 20, 5200);
+INSERT INTO k_decoration_level VALUES (37, 1, 2, 260);
+INSERT INTO k_decoration_level VALUES (37, 2, 5, 410);
+INSERT INTO k_decoration_level VALUES (37, 3, 7, 700);
+INSERT INTO k_decoration_level VALUES (37, 4, 9, 1080);
+INSERT INTO k_decoration_level VALUES (37, 5, 12, 1560);
+INSERT INTO k_decoration_level VALUES (37, 6, 15, 1990);
+INSERT INTO k_decoration_level VALUES (37, 7, 17, 2490);
+INSERT INTO k_decoration_level VALUES (37, 8, 20, 3070);
+INSERT INTO k_decoration_level VALUES (37, 9, 22, 3720);
+INSERT INTO k_decoration_level VALUES (37, 10, 25, 4440);
+INSERT INTO k_decoration_level VALUES (38, 1, 2, 330);
+INSERT INTO k_decoration_level VALUES (38, 2, 4, 540);
+INSERT INTO k_decoration_level VALUES (38, 3, 6, 930);
+INSERT INTO k_decoration_level VALUES (38, 4, 8, 1450);
+INSERT INTO k_decoration_level VALUES (38, 5, 10, 2100);
+INSERT INTO k_decoration_level VALUES (38, 6, 12, 2680);
+INSERT INTO k_decoration_level VALUES (38, 7, 14, 3360);
+INSERT INTO k_decoration_level VALUES (38, 8, 16, 4140);
+INSERT INTO k_decoration_level VALUES (38, 9, 18, 5020);
+INSERT INTO k_decoration_level VALUES (38, 10, 20, 5990);
+INSERT INTO k_decoration_level VALUES (39, 1, 2, 300);
+INSERT INTO k_decoration_level VALUES (39, 2, 4, 460);
+INSERT INTO k_decoration_level VALUES (39, 3, 6, 760);
+INSERT INTO k_decoration_level VALUES (39, 4, 8, 1160);
+INSERT INTO k_decoration_level VALUES (39, 5, 10, 1670);
+INSERT INTO k_decoration_level VALUES (39, 6, 12, 2130);
+INSERT INTO k_decoration_level VALUES (39, 7, 14, 2660);
+INSERT INTO k_decoration_level VALUES (39, 8, 16, 3270);
+INSERT INTO k_decoration_level VALUES (39, 9, 18, 3960);
+INSERT INTO k_decoration_level VALUES (39, 10, 20, 4720);
+INSERT INTO k_decoration_level VALUES (9, 1, 2, 150);
+INSERT INTO k_decoration_level VALUES (9, 2, 4, 375);
+INSERT INTO k_decoration_level VALUES (9, 3, 6, 600);
+INSERT INTO k_decoration_level VALUES (9, 4, 8, 825);
+INSERT INTO k_decoration_level VALUES (9, 5, 10, 1050);
+INSERT INTO k_decoration_level VALUES (9, 6, 12, 1275);
+INSERT INTO k_decoration_level VALUES (9, 7, 14, 1500);
+INSERT INTO k_decoration_level VALUES (9, 8, 16, 1725);
+INSERT INTO k_decoration_level VALUES (9, 9, 18, 1950);
+INSERT INTO k_decoration_level VALUES (9, 10, 20, 2175);
+INSERT INTO k_decoration_level VALUES (7, 1, 2, 80);
+INSERT INTO k_decoration_level VALUES (7, 2, 4, 130);
+INSERT INTO k_decoration_level VALUES (7, 3, 6, 180);
+INSERT INTO k_decoration_level VALUES (7, 4, 8, 230);
+INSERT INTO k_decoration_level VALUES (7, 5, 10, 280);
+INSERT INTO k_decoration_level VALUES (7, 6, 12, 330);
+INSERT INTO k_decoration_level VALUES (7, 7, 14, 380);
+INSERT INTO k_decoration_level VALUES (7, 8, 16, 430);
+INSERT INTO k_decoration_level VALUES (7, 9, 18, 480);
+INSERT INTO k_decoration_level VALUES (7, 10, 20, 530);
+INSERT INTO k_decoration_level VALUES (34, 1, 1, 80);
+INSERT INTO k_decoration_level VALUES (34, 2, 2, 130);
+INSERT INTO k_decoration_level VALUES (34, 3, 3, 180);
+INSERT INTO k_decoration_level VALUES (34, 4, 4, 230);
+INSERT INTO k_decoration_level VALUES (34, 5, 5, 280);
+INSERT INTO k_decoration_level VALUES (34, 6, 6, 330);
+INSERT INTO k_decoration_level VALUES (34, 7, 7, 380);
+INSERT INTO k_decoration_level VALUES (34, 8, 8, 430);
+INSERT INTO k_decoration_level VALUES (34, 9, 9, 480);
+INSERT INTO k_decoration_level VALUES (34, 10, 10, 530);
+INSERT INTO k_decoration_level VALUES (11, 1, 3, 50);
+INSERT INTO k_decoration_level VALUES (11, 2, 6, 100);
+INSERT INTO k_decoration_level VALUES (11, 3, 9, 150);
+INSERT INTO k_decoration_level VALUES (11, 4, 12, 200);
+INSERT INTO k_decoration_level VALUES (11, 5, 15, 250);
+INSERT INTO k_decoration_level VALUES (11, 6, 18, 300);
+INSERT INTO k_decoration_level VALUES (11, 7, 21, 350);
+INSERT INTO k_decoration_level VALUES (11, 8, 24, 400);
+INSERT INTO k_decoration_level VALUES (11, 9, 27, 450);
+INSERT INTO k_decoration_level VALUES (11, 10, 30, 500);
+INSERT INTO k_decoration_level VALUES (5, 1, 5, 40);
+INSERT INTO k_decoration_level VALUES (5, 2, 10, 90);
+INSERT INTO k_decoration_level VALUES (5, 3, 15, 140);
+INSERT INTO k_decoration_level VALUES (5, 4, 20, 190);
+INSERT INTO k_decoration_level VALUES (5, 5, 25, 240);
+INSERT INTO k_decoration_level VALUES (5, 6, 30, 290);
+INSERT INTO k_decoration_level VALUES (5, 7, 35, 340);
+INSERT INTO k_decoration_level VALUES (5, 8, 40, 390);
+INSERT INTO k_decoration_level VALUES (5, 9, 45, 440);
+INSERT INTO k_decoration_level VALUES (5, 10, 50, 490);
+INSERT INTO k_decoration_level VALUES (10, 1, 1, 30);
+INSERT INTO k_decoration_level VALUES (10, 2, 2, 80);
+INSERT INTO k_decoration_level VALUES (10, 3, 3, 140);
+INSERT INTO k_decoration_level VALUES (10, 4, 4, 200);
+INSERT INTO k_decoration_level VALUES (10, 5, 5, 260);
+INSERT INTO k_decoration_level VALUES (10, 6, 6, 320);
+INSERT INTO k_decoration_level VALUES (10, 7, 7, 380);
+INSERT INTO k_decoration_level VALUES (10, 8, 8, 440);
+INSERT INTO k_decoration_level VALUES (10, 9, 9, 500);
+INSERT INTO k_decoration_level VALUES (10, 10, 10, 560);
+INSERT INTO k_decoration_level VALUES (35, 1, 200, 30);
+INSERT INTO k_decoration_level VALUES (35, 2, 400, 80);
+INSERT INTO k_decoration_level VALUES (35, 3, 600, 130);
+INSERT INTO k_decoration_level VALUES (35, 4, 800, 180);
+INSERT INTO k_decoration_level VALUES (35, 5, 1000, 230);
+INSERT INTO k_decoration_level VALUES (35, 6, 1200, 280);
+INSERT INTO k_decoration_level VALUES (35, 7, 1400, 330);
+INSERT INTO k_decoration_level VALUES (35, 8, 1600, 380);
+INSERT INTO k_decoration_level VALUES (35, 9, 1800, 430);
+INSERT INTO k_decoration_level VALUES (35, 10, 2000, 480);
+
+
 INSERT INTO k_building VALUES(1, 'Summoners Tower', '');
 INSERT INTO k_building VALUES(2, 'Summonhenge', '');
 INSERT INTO k_building VALUES(3, 'Pond of Mana', '');
@@ -413,6 +671,9 @@ INSERT INTO k_inventory VALUES(5004, 29, 'Crystal of Light', '');
 INSERT INTO k_inventory VALUES(5005, 29, 'Crystal of Dark', '');
 INSERT INTO k_inventory VALUES(6001, 29, 'Crystal of Magic', '');
 INSERT INTO k_inventory VALUES(7001, 29, 'Crystal of Pure', '');
+INSERT INTO k_inventory VALUES(8002, 29, 'Ancient Magic Origin', '');
+INSERT INTO k_inventory VALUES(9002, 27, 'Karzhan''s Rune Ore', '');
+INSERT INTO k_inventory VALUES(8001, 27, 'Ancient Magic Stone', '');
 INSERT INTO k_inventory VALUES(142110115, 61, 'Water Angelmon', '');
 INSERT INTO k_inventory VALUES(142120115, 61, 'Fire Angelmon', '');
 INSERT INTO k_inventory VALUES(142130115, 61, 'Wind Angelmon', '');

+ 91 - 0
install_base.py

@@ -388,6 +388,97 @@ def createDatabase(name):
                 value INT
             );
         ''')
+        cursor.execute('''
+            CREATE TABLE IF NOT EXISTS run(
+                uid TEXT,
+                id INT,
+                dtime TIMESTAMP,
+                area INT,
+                stage INT,
+                difficulty INT,
+                win INT,
+                time INT,
+                mana INT,
+                energy INT,
+                crystal INT,
+                helper INT
+            );
+        ''')
+        cursor.execute('''
+            CREATE TABLE IF NOT EXISTS run_party(
+                run INT,
+                unit INT,
+                k_unit INT
+            );
+        ''')
+        cursor.execute('''
+            CREATE TABLE IF NOT EXISTS run_drop_rune(
+                run INT,
+                id INT,
+                type TEXT,
+                slot INT,
+                stars INT,
+                ancient INT,
+                quality INT,
+                value INT,
+                efficiency INT,
+                main_stat TEXT,
+                main_stat_value INT,
+                innate_stat TEXT,
+                innate_stat_value INT,
+                substat_1 TEXT,
+                substat_1_value INT,
+                substat_2 TEXT,
+                substat_2_value INT,
+                substat_3 TEXT,
+                substat_3_value INT,
+                substat_4 TEXT,
+                substat_4_value INT
+            );
+        ''')
+        cursor.execute('''
+            CREATE TABLE IF NOT EXISTS run_drop_rune_craft(
+                run INT,
+                id INT,
+                type INT,
+                quality INT,
+                rune INT,
+                stat INT,
+                value INT
+            );
+        ''')
+        cursor.execute('''
+            CREATE TABLE IF NOT EXISTS run_drop_item(
+                run INT,
+                item INT,
+                quantity INT
+            );
+        ''')
+        cursor.execute('''
+            CREATE TABLE IF NOT EXISTS run_drop_unit(
+                run INT,
+                unit INT
+            );
+        ''')
+        cursor.execute('''
+            CREATE TABLE IF NOT EXISTS run_drop_sd(
+                run INT,
+                unit INT
+            );
+        ''')
+        cursor.execute('''
+            CREATE TABLE IF NOT EXISTS run_drop_unit_pieces(
+                run INT,
+                unit INT,
+                quantity INT
+            );
+        ''')
+        cursor.execute('''
+            CREATE TABLE IF NOT EXISTS run_drop_shapeshifting(
+                run INT,
+                quantity INT
+            );
+        ''')
         db.commit()
         cursor.close()
         return db

+ 22 - 0
public/API/v1/log-run/index.php

@@ -0,0 +1,22 @@
+<?php
+    global $path;
+    error_log("API v1 log-run REQUEST");
+    foreach ($_POST as $param_name => $param_val) {
+        error_log("    Param: $param_name; Value: $param_val");
+    }
+    if (isset($_POST["data"])){
+        $data = $_POST["data"];
+        if (json_decode($data) === null){
+            echo ("ERROR: Invalid JSON");
+            error_log("API v1 log-run REQUEST INVALID JSON");
+        }
+        else{
+            error_log($_SERVER["DOCUMENT_ROOT"] . "/../application/bin/run-log.py " . escapeshellarg($data));
+            exec($_SERVER["DOCUMENT_ROOT"] . "/../application/bin/run-log.py " . escapeshellarg($data));
+        }
+    }
+    else{
+        echo ("ERROR: No POST data");
+        error_log("API v1 log-run REQUEST NO POST DATA");
+    }
+?>

+ 113 - 0
public/css/runs.css

@@ -0,0 +1,113 @@
+table#runs{
+    color: #ffffff;
+    border-collapse: collapse;
+    width: 100%;
+}
+
+table#runs td.area, td.date, td.team, td.time, td.reward{
+    border-top: 0.1em solid #99999977;
+}
+
+table#runs td.area div.area{
+    position: relative;
+    width: 2em;
+    height: 2em;
+}
+
+table#runs td.area div.area img.area{
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 2em;
+    height: 2em;
+    border: 0.2em solid #555555;
+    border-radius: 0.5em;
+    background-color: #777777;
+}
+
+table#runs td.area div.area span.stage{
+    position: absolute;
+    top: 1.3em;
+    left: 0;
+    width: 2em;
+    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;
+}
+
+table#runs td.area div.area span.difficulty{
+    position: absolute;
+    top: 0em;
+    left: 0;
+    width: 1.8em;
+    text-align: right;
+}
+table#runs td.area div.area span.difficulty img.difficulty{
+    height: 0.6em;
+    width: 0.6em;
+    margin: 0 -0.3em;
+    filter: sepia(100%) saturate(500%) hue-rotate(10deg) saturate(200%) drop-shadow(0 0 0.1em #000) drop-shadow(0 0 0.1em #000) drop-shadow(0 0 0.1em #000);
+}
+
+table#runs td.team{
+    font-size: 30%;
+}
+
+table#runs td.team div.helper{
+    vertical-align: top;
+    background-color: #777777;
+}
+
+table#runs td.team div.monster_panel.disabled{
+    opacity: 0.5;
+}
+
+table#runs td.time{
+    font-weight: bold;
+}
+
+table#runs td.time span.ms{
+    font-size: 70%;
+}
+
+table#runs td.time span.defeated{
+    color: #cc0000;
+    text-shadow: 0 0 0.05em #ffffff;
+}
+
+table#runs td.reward div.item{
+    display: inline-block;
+    width: 1.6em;
+    height: 1.6em;
+    position: relative;
+    border: 0.2em solid #555555;
+    border-radius: 0.5em;
+    background-color: #777777;
+}
+
+table#runs td.reward div.item img.item{
+    width: 1.6em;
+    height: 1.6em;
+    border-radius: 0.4em;
+}
+
+table#runs td.reward div.item span.item{
+    position: absolute;
+    left: 0;
+    top: 1.6em;
+    font-size: 60%;
+    width: 2.6em;
+    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;
+}
+
+table#runs td.reward div.item_rune table.rune{
+    display: none;
+    position: absolute;
+    right: 1.8em;
+    bottom: 0;
+    font-size: 75%;
+}
+
+table#runs td.reward div.item_rune:hover table.rune{
+    display: block;
+}

+ 1 - 1
public/css/ui.css

@@ -278,7 +278,7 @@ input[type=button], input[type=submit]{
     cursor: pointer;
     box-shadow: 0 0 0.3em #000000;
 }
-input[type=text], input[type=number]{
+input[type=text], input[type=number], input[type=date]{
     font-size: 80%;
     font-weight: bold;
     padding: 0 0.3em;

+ 736 - 0
swex-plugin/swdb.js

@@ -0,0 +1,736 @@
+//const fs = require('fs-extra');
+//const csv = require('fast-csv');
+const dateFormat = require('dateformat');
+//const path = require('path');
+//const sanitize = require('sanitize-filename');
+
+module.exports = {
+    defaultConfig: {
+        enabled: true,
+        debug: true
+    },
+    pluginName: 'SWDB',
+    pluginDescription: 'Saves runs to SWDB.',
+    target_host: 'http://localhost',
+    target_port: '80',
+    target_url: '/API/v1/log-run',
+    temp: {},
+    proxy: null,
+    config: null,
+
+    /**
+     * Initializes he plugin.
+     * 
+     * @param proxy The proxy.
+     * @param Global configurations.
+     */
+    init(proxy, config) {
+        this.proxy = proxy;
+        this.config = config;
+        proxy.on('apiCommand', (req, resp) => {
+            try {
+                wizardID = '';
+                command = '';
+                if (config.Config.Plugins[this.pluginName].enabled) {
+                    //const { command, wizard_id: wizardID } = req;
+                    command = req["command"];
+                    wizardID = req["wizard_id"];
+                }
+                if (!this.temp[wizardID]) {
+                    this.temp[wizardID] = {};
+                }
+                if (command === 'BattleScenarioStart') {
+                    //for (var key in req) {
+                    //    this.log('DEBUG', `IVV SCE START: ${key}: ${req[key]}`);
+                    //}
+                    if (req["helper_list"] != null || req["mentor_helper_list"] != null || req["npc_friend_helper_list"] != null ){
+                        this.temp[wizardID].helper = 1;
+                    }
+                    else{
+                        this.temp[wizardID].helper = 0;
+                    }
+                    this.temp[wizardID].area = req.region_id;
+                    this.temp[wizardID].stage = req.stage_no;
+                    this.temp[wizardID].difficulty = req.difficulty;
+                }
+
+                if (command === 'BattleScenarioResult' || command === 'BattleDungeonResult' || command === 'BattleDimensionHoleDungeonResult') {
+                    //proxy.log({ type: 'info', source: 'plugin', name: this.pluginName, message: `IVV RUN RESULT: ${resp}` })
+                    for (var key in req) {
+                        this.log("info", `IVV REQ: ${key}: ${req[key]}`);
+                    }
+                    for (var key in resp) {
+                        this.log("info", `IVV RES: ${key}: ${resp[key]}`);
+                    }
+                    this.parse_normal(req, resp);
+                }
+
+                if (command === 'BattleRiftOfWorldsRaidResult') {
+                    this.parse_raid_rift(req, resp);
+                }
+
+                if (command === 'BattleRiftDungeonResult') {
+                    this.parse_elemental_rift(req, resp);
+                }
+            }
+            catch (e) {
+                proxy.log({ type: 'error', source: 'plugin', name: this.pluginName, message: `An unexpected error occured: ${e.message}` });
+            }
+        });
+    },
+
+    /**
+     * Parses a reward item (no runes!).
+     * 
+     * @param crate The crate object.
+     * @return array of {item, unit, pieces, quantity, shapeshifting}
+     */
+    getItem(crate) {
+        item = 0;
+        unit = 0;
+        pieces = 0;
+        quantity = 0;
+        shapeshifting = 0;
+        if (crate.random_scroll && (crate.random_scroll.item_master_id === 1 || crate.random_scroll.item_master_id === 8 || crate.random_scroll.item_master_id === 2)){
+            // 1: Unknown scroll
+            // 2: Mystical Scroll
+            // 8: Summoning Stones
+            item = crate.random_scroll.item_master_id;
+            quantity = crate.random_scroll.item_quantity;
+        }
+        if (crate.costume_point) {
+            shapeshifting = crate.costume_point;
+        }
+        if (crate.rune_upgrade_stone) {
+            item = 'Power Stone'; // TODO: Do they exist?
+            quantity = crate.rune_upgrade_stone.item_quantity;
+        }
+        if (crate.unit_info) {
+            unit = crate.unit_info.unit_master_id;
+        }
+        if (crate.material) {
+            item = crate.material.item_master_id;
+            quantity = crate.material.item_quantity;
+        }
+        if (crate.summon_pieces) {
+            pieces = crate.summon_pieces.item_master_id;
+            quantity = crate.summon_pieces.item_quantity;
+        }
+        if (crate.craft_stuff) {
+            item = crate.craft_stuff.item_master_id;
+            quantity = crate.craft_stuff.item_quantity;
+        }
+        drop = {item, unit, pieces, quantity, shapeshifting}
+        return drop;
+    },
+
+    /**
+     * TODO! Unaddapted.
+     * Parses a rift reward item (no runes!).
+     * 
+     * @param crate The crate object.
+     * @return array of {item, unit, pieces, quantity}
+     */
+    getItemRift(item, entry) {
+        if (item.type === 8) {
+            const rune = item.info;
+            entry.drop = 'Rune';
+            entry.grade = `${rune.class}*`;
+            entry.sell_value = rune.sell_value;
+            entry.set = gMapping.rune.sets[rune.set_id];
+            entry.slot = rune.slot_no;
+            entry.efficiency = gMapping.getRuneEfficiency(rune).current;
+            entry.rarity = gMapping.rune.class[rune.sec_eff.length];
+            entry.main_stat = gMapping.getRuneEffect(rune.pri_eff);
+            entry.prefix_stat = gMapping.getRuneEffect(rune.prefix_eff);
+
+            rune.sec_eff.forEach((substat, i) => {
+                entry[`sub${i + 1}`] = gMapping.getRuneEffect(substat);
+            });
+        }
+        if (item.info.craft_type_id) {
+            enhancement = this.getEnchantVals(item.info.craft_type_id, item.info.craft_type);
+            entry.drop = enhancement.drop;
+            entry.sell_value = item.sell_value;
+            entry.set = enhancement.set;
+            entry.main_stat = enhancement.type;
+            entry.sub1 = enhancement.min;
+            entry.sub2 = enhancement.max;
+        }
+        return entry;
+    },
+
+    /**
+     * TODO! Will I need this.
+     * Saves run data to a file in CSV format.
+     * 
+     */
+    saveToFile(entry, filename, headers, proxy) {
+        const csvData = [];
+        const self = this;
+        fs.ensureFile(path.join(config.Config.App.filesPath, filename), err => {
+        if (err) {
+            return;
+        }
+        csv
+            .fromPath(path.join(config.Config.App.filesPath, filename), { ignoreEmpty: true, headers, renameHeaders: true })
+            .on('data', data => {
+            csvData.push(data);
+            })
+            .on('end', () => {
+            csvData.push(entry);
+            csv.writeToPath(path.join(config.Config.App.filesPath, filename), csvData, { headers }).on('finish', () => {
+                proxy.log({ type: 'success', source: 'plugin', name: self.pluginName, message: `Saved run data to ${filename}` });
+            });
+            });
+        });
+    },
+
+    /**
+     * Parses a battle data.
+     * Valid for scenario, dungeons and Dimensional Hole.
+     *
+     * @param req The request
+     * @param resp The response.
+     */
+    parse_normal(req, resp) {
+        const { command } = req;
+        const { wizard_id: wizardID, wizard_name: wizardName } = resp.wizard_info;
+
+        const run = {};
+        const run_party = {};
+        const run_k_party = {};
+        log = true;
+
+        if (command === 'BattleDungeonResult') {
+            //for (var key in req) {
+            //    this.log("DEBUG", `IVV DUNGEON REQ: ${key}: ${req[key]}`);
+            //}
+            if (req.dungeon_id > 10000){
+                // Hall of Heroes or unknown
+                log = false;
+            }
+            run.area = req.dungeon_id;
+            run.stage = req.stage_id;
+            run.difficulty = 0
+            // TODO: Read request
+            run.helper = 0;
+        }
+
+        if (command === 'BattleScenarioResult') {
+            //for (var key in this.temp[wizardID]) {
+            //    this.log("DEBUG", `IVV TEMP: ${key}: ${this.temp[wizardID][key]}`);
+            //}
+            //for (var key in req) {
+            //    this.log("DEBUG", `IVV REQ: ${key}: ${req[key]}`);
+            //}
+            run.helper = this.temp[wizardID].helper;
+            run.area = this.temp[wizardID].area;
+            run.stage = this.temp[wizardID].stage;
+            run.difficulty = this.temp[wizardID].difficulty;
+        }
+
+        if (command === 'BattleDimensionHoleDungeonResult') {
+            //if (gMapping.dungeon[resp.dungeon_id]) {
+            //    run.dungeon = `${gMapping.dungeon[resp.dungeon_id]} Level ${resp.difficulty}`;
+            //}
+            //for (var key in req) {
+            //    this.log("DEBUG", `IVV DIMENSONALE REQ: ${key}: ${req[key]}`);
+            //}
+            run.area = resp.dungeon_id;
+            run.stage = resp.difficulty;
+            run.difficulty = 0
+            run.helper = 0;
+        }
+        run.uid = wizardID;
+        run.win = resp.win_lose; // 1/0 2 is lost in scenario
+
+        run.dtime = dateFormat(new Date(), 'yyyy-mm-dd HH:MM:ss');
+
+        const reward = resp.reward ? resp.reward : {};
+        run.time = req.clear_time ? req.clear_time : 0;
+
+        run.mana = reward.mana ? reward.mana : 0;
+        run.energy = reward.energy ? reward.energy : 0;
+        run.crystal = reward.crystal ? reward.crystal : 0;
+        
+        // TODO: GET
+        //run.helper = 0;
+
+        run.rune = {};
+        run.unit = 0;
+        run.unit_pieces = {};
+        run.sd = 0;
+        run.item = {};
+        run.shapeshifting = 0;
+
+        if (reward.crate) {
+            run.mana = reward.crate.mana ? run.mana + reward.crate.mana : run.mana;
+            run.energy = reward.crate.energy ? run.energy + reward.crate.energy : run.energy;
+            run.crystal = reward.crate.crystal ? run.crystal + reward.crate.crystal : run.crystal;
+
+            if (reward.crate.rune) {
+                const rune = reward.crate.rune;
+                //for (var key in rune) {
+                //    this.proxy.log({ type: 'info', source: 'plugin', name: this.pluginName, message: `IVV RUNE: ${key}: ${rune[key]}` })
+                //}
+                run.rune.id = rune.rune_id;
+                run.rune.grade = rune.class;
+                run.rune.value = rune.sell_value;
+                run.rune.set = rune.set_id;
+                run.rune.slot = rune.slot_no;
+                run.rune.efficiency = gMapping.getRuneEfficiency(rune).current;
+                run.rune.quality = rune.rank;
+                run.rune.main = rune.pri_eff[0];
+                run.rune.main_value = rune.pri_eff[1];
+                if (rune.prefix_eff){
+                    run.rune.innate = rune.prefix_eff[0];
+                    run.rune.innate_value = rune.prefix_eff[1];
+                }
+                run.rune['substat_1'] = 0;
+                run.rune['substat_1_value'] = 0;
+                run.rune['substat_2'] = 0;
+                run.rune['substat_2_value'] = 0;
+                run.rune['substat_3'] = 0;
+                run.rune['substat_3_value'] = 0;
+                run.rune['substat_4'] = 0;
+                run.rune['substat_4_value'] = 0;
+                rune.sec_eff.forEach((substat, i) => {
+                    run.rune[`substat_${i + 1}`] = substat[0];
+                    run.rune[`substat_${i + 1}_value`] = substat[1];
+                });
+            }
+            else {
+                for (var key in reward.crate) {
+                    this.proxy.log({ type: 'info', source: 'plugin', name: this.pluginName, message: `IVV ITEM: ${key}: ${reward.crate[key]}` })
+                }
+                drop = this.getItem(reward.crate);
+                if (drop.unit){
+                    run.unit.id = drop.unit;
+                }
+                if (drop.pieces){
+                    run.unit_pieces.id = drop.pieces;
+                    run.unit_pieces.quantity = drop.quantity;
+                }
+                if (drop.item){
+                    run.item.id = drop.item;
+                    run.item.quantity = drop.quantity;
+                }
+                run.shapeshifting = drop.shapeshifting;
+            }
+        }
+        if (resp.instance_info) {
+            run.sd = resp.instance_info;
+        }
+
+        if (resp.unit_list && resp.unit_list.length > 0) {
+            resp.unit_list.forEach((unit, i) => {
+                run_party[i] = unit.unit_master_id;
+                run_k_party[i] = unit.unit_id;
+            });
+        }
+
+        json_rune = '[';
+        if (run.rune.id != undefined){
+            json_rune += '{';
+            json_rune += `"id": "${run.rune.id}", `;
+            json_rune += `"stars": "${run.rune.grade}", `;
+            json_rune += `"type": "${run.rune.set}", `;
+            json_rune += `"slot": "${run.rune.slot}", `;
+            json_rune += `"efficiency": "${run.rune.efficiency}", `;
+            json_rune += `"quality": "${run.rune.quality}", `;
+            // TODO: Get
+            json_rune += `"ancient": 0, `;
+            json_rune += `"value": "${run.rune.value}", `;
+            json_rune += `"main_stat": "${run.rune.main}", `;
+            json_rune += `"main_stat_value": "${run.rune.main_value}", `;
+            json_rune += `"innate_stat": "${run.rune.innate}", `;
+            json_rune += `"innate_stat_value": "${run.rune.innate_value}", `;
+            json_rune += `"substat_1": "${run.rune.substat_1}", `;
+            json_rune += `"substat_1_value": "${run.rune.substat_1_value}", `;
+            json_rune += `"substat_2": "${run.rune.substat_2}", `;
+            json_rune += `"substat_2_value": "${run.rune.substat_2_value}", `;
+            json_rune += `"substat_3": "${run.rune.substat_3}", `;
+            json_rune += `"substat_3_value": "${run.rune.substat_3_value}", `;
+            json_rune += `"substat_4": "${run.rune.substat_4}", `;
+            json_rune += `"substat_4_value": "${run.rune.substat_4_value}"`;
+            json_rune += '}';
+        }
+        json_rune = json_rune + "]";
+
+        json_item = '[';
+        if (run.item.id != undefined){
+            json_item += '{';
+            json_item += `"id": "${run.item.id}", `;
+            json_item += `"quantity": "${run.item.quantity}"`;
+            json_item += '}';
+        }
+        json_item = json_item + "]";
+
+        json_unit_pieces = '[';
+        if (run.unit_pieces.length > 0){
+            json_unit_pieces += '{';
+            json_unit_pieces += `"id": "${run.unit_pieces.id}", `;
+            json_unit_pieces += `"quantity": "${run.unit_pieces.quantity}"`;
+            json_unit_pieces += '}';
+        }
+        json_unit_pieces = json_unit_pieces + "]";
+
+        json_party = '[';
+        resp.unit_list.forEach((unit, i) => {
+            json_party = json_party + '{"unit_id": ' + run_party[i] + ', "unit_master_id": ' + run_k_party[i] + '},';
+        });
+        json_party = json_party.slice(0, -1) + "]";
+
+        json = `{`;
+        json += `"uid": "${run.uid}", `;
+        json += `"dtime": "${run.dtime}", `;
+        json += `"area": "${run.area}", `;
+        json += `"stage": "${run.stage}", `;
+        json += `"difficulty": "${run.difficulty}", `;
+        json += `"win": "${run.win}", `;
+        json += `"time": "${run.time}", `;
+        json += `"mana": "${run.mana}", `;
+        json += `"energy": "${run.energy}", `;
+        json += `"crystal": "${run.crystal}", `;
+        json += `"rune": ${json_rune}, `;
+        json += `"sd": ${run.sd}, `;
+        json += `"item": ${json_item}, `;
+        json += `"unit": ${run.unit}, `;
+        json += `"unit_pieces": ${json_unit_pieces}, `;
+        json += `"shapeshifting": ${run.shapeshifting}, `;
+        json += `"party": ${json_party}, `;
+        json += `"helper": "${run.helper}"`;
+        json += `}`;
+        this.log('DEBUG', `json: ${json}`)
+        //const filename = sanitize(`${wizardName}-${wizardID}-runs.csv`);
+        //const filename = sanitize(`IVV-${wizardName}-${wizardID}-runs.csv`);
+        //this.saveToFile(entry, filename, headers, proxy);
+        
+        // Make the request
+        //var inspect = require('eyespect').inspector();
+        var request = require('request')
+
+        var postData = {
+            name: 'data',
+            value: json
+        }
+
+        var querystring = require('querystring');
+        var http = require('http');
+        var fs = require('fs');
+        var post_data = querystring.stringify({
+            'data' : json
+        });
+
+        // An object of options to indicate where to post to
+        var post_options = {
+            host: '192.168.1.101',//this.target_host,
+            port: this.target_port,
+            path: this.target_url,
+            method: 'POST',
+            headers: {
+                'Content-Type': 'application/x-www-form-urlencoded',
+                'Content-Length': Buffer.byteLength(post_data)
+            }
+        };
+
+        // Set up the request
+        var resp = ""
+        var post_req = http.request(post_options, function(res) {
+            res.setEncoding('utf8');
+            res.on('data', function (chunk) {
+                resp = chunk;
+            });
+        });
+        
+
+        // post the data
+        post_req.write(post_data);
+        post_req.end();
+        
+        
+    },
+
+    /**
+     * TODO Unaddapted.
+     * Parses a battle data.
+     * Valid for Rift Raids.
+     *
+     * @param req The request
+     * @param resp The response.
+     */
+    parse_raid_rift(req, resp) {
+        const { wizard_id: wizardID, wizard_name: wizardName } = resp.wizard_info;
+
+        let run = {};
+        if (gMapping.dungeon[req.dungeon_id]) {
+            run.dungeon = `${gMapping.elemental_rift_dungeon[req.dungeon_id]}`;
+            isElemental = true;
+        }
+
+        //const winLost = resp.win_lose === 1 ? 'Win' : 'Did not kill';
+
+        run.dtime = dateFormat(new Date(), 'yyyy-mm-dd HH:MM:ss');
+        //run.result = winLost;
+
+        run.win  = resp.win_lose;
+        run.rune = {};
+        run.unit = 0;
+        run.unit_pieces = {};
+        run.sd = 0;
+        run.item = {};
+        run.shapeshifting = 0;
+        run.time = req.clear_time ? req.clear_time : 0;
+
+        const reward = resp.reward ? resp.reward : {};
+
+        if (resp.win_lose === 1) {
+            // No crate: Mana of shapeshifting stones
+            if (!reward.crate) {
+                const reward = resp.battle_reward_list.find(value => value.wizard_id === resp.wizard_info.wizard_id).reward_list[0];
+                if (reward.item_master_id === 6) {
+                    run.shapeshifting = reward.item_quantity;
+                }
+                else {
+                    run.mana = reward.item_quantity;
+                }
+            }
+            // Rune craft item
+            else if (reward.crate.changestones) {
+                const item = {
+                    info: {
+                        craft_type: reward.crate.changestones[0].craft_type,
+                        craft_type_id: reward.crate.changestones[0].craft_type_id
+                    },
+                    sell_value: reward.crate.changestones[0].sell_value
+                };
+                run.drop= this.getItemRift(item, run);
+            }
+            // Rune
+            else if (reward.crate.rune) {
+                const rune = reward.crate.rune;
+                //for (var key in rune) {
+                //    this.proxy.log({ type: 'info', source: 'plugin', name: this.pluginName, message: `IVV RUNE: ${key}: ${rune[key]}` })
+                //}
+                run.rune.id = rune.rune_id;
+                run.rune.grade = rune.class;
+                run.rune.value = rune.sell_value;
+                run.rune.set = rune.set_id;
+                run.rune.slot = rune.slot_no;
+                run.rune.efficiency = gMapping.getRuneEfficiency(rune).current;
+                run.rune.quality = rune.rank;
+                run.rune.main = rune.pri_eff[0];
+                run.rune.main_value = rune.pri_eff[1];
+                if (rune.prefix_eff){
+                    run.rune.innate = rune.prefix_eff[0];
+                    run.rune.innate_value = rune.prefix_eff[1];
+                }
+                rune.sec_eff.forEach((substat, i) => {
+                    run.rune[`substat_${i + 1}`] = substat[0];
+                    run.rune[`substat_${i + 1}_value`] = substat[1];
+                });
+            }
+            // Unit (Rainbowmon)
+            else if (reward.crate.unit_info && reward.crate.unit_info.unit_master_id > 0) {
+                run.unit.id = reward.crate.unit_info.unit_master_id;
+            }
+            // Item
+            else if (!run.drop && resp.reward.crate) {
+                //run.drop = this.getItem(reward.resp.reward.crate);
+                if (resp.reward.crate.random_scroll && (resp.reward.crate.random_scroll.item_master_id === 1 || resp.reward.crate.random_scroll.item_master_id === 8 || resp.reward.crate.random_scroll.item_master_id === 2)){
+                    // 1: Unknown scroll
+                    // 2: Mystical Scroll
+                    // 8: Summoning Stones
+                    run.item.id = resp.reward.crate.random_scroll.item_master_id;
+                    run.item.quantity = resp.reward.crate.random_scroll.item_quantity;
+                }
+                //if (resp.reward.crate.costume_point) {
+                //    shapeshifting = resp.reward.crate.costume_point;
+                //}
+                //if (resp.reward.crate.rune_upgrade_stone) {
+                //    item = 'Power Stone'; // TODO: Do they exist?
+                //    quantity = resp.reward.crate.rune_upgrade_stone.item_quantity;
+                //}
+                //if (resp.reward.crate.unit_info) {
+                //    unit = resp.reward.crate.unit_info.unit_master_id;
+                //}
+                if (resp.reward.crate.material) {
+                    item = resp.reward.crate.material.item_master_id;
+                    quantity = resp.reward.crate.material.item_quantity;
+                }
+                //if (resp.reward.crate.summon_pieces) {
+                //    pieces = resp.reward.crate.summon_pieces.item_master_id;
+                //    quantity = resp.reward.crate.summon_pieces.item_quantity;
+                //}
+                if (resp.reward.crate.craft_stuff) {
+                    item = resp.reward.crate.craft_stuff.item_master_id;
+                    quantity = resp.reward.crate.craft_stuff.item_quantity;
+                }
+            }
+            else {
+                run.drop = 'unknown';
+            }
+        }
+
+        if (resp.unit_list && resp.unit_list.length > 0) {
+            resp.unit_list.forEach((unit, i) => {
+                run_party[i] = unit.unit_master_id;
+                run_k_party[i] = unit.unit_id;
+            });
+        }
+
+
+        //const filename = sanitize(`${wizardName}-${wizardID}-raid-runs.csv`);
+        //this.saveToFile(run. filename, headers, proxy);
+    },
+
+    /**
+     * TODO Unaddapted.
+     * Parses a battle data.
+     * Valid for Rift Dungeons.
+     *
+     * @param req The request
+     * @param resp The response.
+     */
+    parse_elemental_rift(req, resp) {
+        const { wizard_id: wizardID, wizard_name: wizardName } = resp.wizard_info;
+
+        let entry = {};
+        if (gMapping.dungeon[req.dungeon_id]) {
+            entry.dungeon = `${gMapping.elemental_rift_dungeon[req.dungeon_id]}`;
+            isElemental = true;
+        }
+
+        const winLost = req.battle_result === 1 ? 'Win' : 'Did not kill';
+
+        entry.date = dateFormat(new Date(), 'yyyy-mm-dd HH:MM');
+        entry.result = winLost;
+
+        if (resp.item_list && resp.item_list.length > 0) {
+            resp.item_list.forEach((item, i) => {
+                if (item.is_boxing !== 1 || item.id === 2001) {
+                entry[`item${i + 1}`] = `${gMapping.craftMaterial[item.id]} x${item.quantity}`;
+                } else {
+                if (item.id === 2) {
+                    entry.drop = 'Mystical Scroll';
+                }
+                if (item.id === 8) {
+                    entry.drop = `Summoning Stones x${item.item_quantity}`;
+                }
+                if (item.info && item.info.unit_master_id > 0) {
+                    entry.drop = `${gMapping.getMonsterName(item.info.unit_master_id)} ${item.class}`;
+                }
+                if ((item.info && item.info.craft_type_id) || item.type === 8) {
+                    entry = this.getItemRift(item, entry);
+                }
+                }
+            });
+        }
+
+        if (resp.unit_list && resp.unit_list.length > 0) {
+            resp.unit_list.forEach((unit, i) => {
+                entry[`team${i + 1}`] = gMapping.getMonsterName(unit.unit_master_id);
+            });
+        }
+        json_rune = '[';
+        if (run.rune.length() > 0){
+            json_rune += `"id": "${run.rune.id}", `;
+            json_rune += `"grade": "${run.rune.grade}", `;
+            json_rune += `"set": "${run.rune.set}", `;
+            json_rune += `"slot": "${run.rune.slot}", `;
+            json_rune += `"efficiency": "${run.rune.efficiency}", `;
+            json_rune += `"quality": "${run.rune.quality}", `;
+            json_rune += `"value": "${run.rune.value}", `;
+            json_rune += `"main_stat": "${run.rune.main}", `;
+            json_rune += `"main_stat_value": "${run.rune.main_value}", `;
+            json_rune += `"innate_stat": "${run.rune.innate}", `;
+            json_rune += `"innate_stat_value": "${run.rune.innate_value}", `;
+            json_rune += `"substat_1": "${run.rune.substat_1}", `;
+            json_rune += `"substat_1_value": "${run.rune.substat_1_value}", `;
+            json_rune += `"substat_2": "${run.rune.substat_2}", `;
+            json_rune += `"substat_2_value": "${run.rune.substat_2_value}", `;
+            json_rune += `"substat_3": "${run.rune.substat_3}", `;
+            json_rune += `"substat_4_value": "${run.rune.substat_3_value}", `;
+            json_rune += `"substat_4": "${run.rune.substat_4}", `;
+            json_rune += `"substat_4_value": "${run.rune.substat_4_value}", `;
+        }
+        json_rune = json_party.slice(0, -1) + "]";
+
+        json_item = '[';
+        if (run.item.length() > 0){
+            json_item += `"id": "${run.item.id}", `;
+            json_item += `"quantity": "${run.item.quantity}", `;
+        }
+        json_item = json_party.slice(0, -1) + "]";
+
+        json_unit_pieces = '[';
+        if (run.unit_pieces.length() > 0){
+            json_unit_pieces += `"id": "${run.unit_pieces.id}", `;
+            json_unit_pieces += `"quantity": "${run.unit_pieces.quantity}", `;
+        }
+        json_unit_pieces = json_party.slice(0, -1) + "]";
+
+        json_party = '[';
+        resp.unit_list.forEach((unit, i) => {
+            json_party = json_party + '{"unit_id": ' + run_party[i] + ', "unit_master_id": ' + run_k_party[i] + '},';
+        });
+        json_party = json_party.slice(0, -1) + "]";
+
+        //this.log('DEBUG', `IVV json_party: ${json_party}`)
+
+        json = `{`;
+        json += `"dtime": "${run.dtime}", `;
+        json += `"area": "${run.dungeon}", `;
+        json += `"stage": "${run.stage}", `;
+        json += `"difficulty": "${run.difficulty}", `;
+        json += `"win": "${run.win}", `;
+        json += `"time": "${run.time}", `;
+        json += `"mana": "${run.mana}", `;
+        json += `"energy": "${run.energy}", `;
+        json += `"crystal": "${run.crystal}", `;
+        json += `"rune": ${json_rune}, `;
+        json += `"sd": ${run.sd}, `;
+        json += `"item": ${json_item}, `;
+        json += `"unit": ${run.unit}, `;
+        json += `"unit_pieces": ${json_unit_pieces}, `;
+        json += `"sapeshifting": ${run.sapeshifting}, `;
+        json += `"party": ${json_party}`;
+        json += `}`;
+        this.log('DEBUG', `json: ${json}`)
+    },
+
+    /**
+     * TODO ???
+     */
+    getEnchantVals(craftID, craftType) {
+        const map = {};
+        const typeNumber = Number(craftID.toString().slice(-4, -2));
+        map.set = gMapping.rune.sets[Number(craftID.toString().slice(0, -4))];
+        map.grade = gMapping.rune.quality[Number(craftID.toString().slice(-1))];
+        map.type = gMapping.rune.effectTypes[typeNumber];
+
+        if (craftType === 2) {
+            map.min = gMapping.grindstone[typeNumber].range[Number(craftID.toString().slice(-1))].min;
+            map.max = gMapping.grindstone[typeNumber].range[Number(craftID.toString().slice(-1))].max;
+            map.drop = 'Grindstone';
+        } else {
+            map.min = gMapping.enchanted_gem[typeNumber].range[Number(craftID.toString().slice(-1))].min;
+            map.max = gMapping.enchanted_gem[typeNumber].range[Number(craftID.toString().slice(-1))].max;
+            map.drop = 'Enchanted Gem';
+        }
+        return map;
+    },
+
+    /**
+     * Prints a messageto the SWEX output.
+     * 
+     * @param type Log type. Debug type will be printed depending on config.
+     * @param msg The message to log.
+     */
+    log(type, msg){
+        //if (type != "debug" || this.config.Config.Plugins[this.pluginName].debug == true){
+            this.proxy.log({ type: type, source: 'plugin', name: this.pluginName, message: msg });
+        //}
+    }
+};