about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorana <ana@ana.st>2021-08-08 16:51:59 +0200
committerana <ana@ana.st>2021-08-08 16:51:59 +0200
commit903aeb762b12fc4e855d2a4c9d172c79b3f864b3 (patch)
treeaa8d4d0623dbc0fa0d9c896214c9b58d896492c6
parent986068d95eb1c4101e5c56c92baab961455fddc0 (diff)
downloadeureka-marks-903aeb762b12fc4e855d2a4c9d172c79b3f864b3.tar.gz
feat: load from csv, etc etc
-rw-r--r--.gitignore2
-rw-r--r--csv/.gitkeep0
-rw-r--r--package-lock.json98
-rw-r--r--package.json3
-rw-r--r--scan-csv.js94
-rw-r--r--src/App.svelte34
-rw-r--r--src/bestiary.js47
-rw-r--r--src/ew.js141
-rw-r--r--src/pagos.bestiary.json1
-rw-r--r--src/stores.js4
-rw-r--r--src/times.js7
11 files changed, 425 insertions, 6 deletions
diff --git a/.gitignore b/.gitignore
index 8e5ee43..9394137 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,5 @@
 build
 node_modules
 public/pure.css
+csv/*
+!csv/.gitkeep
diff --git a/csv/.gitkeep b/csv/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/csv/.gitkeep
diff --git a/package-lock.json b/package-lock.json
index e382f58..142c935 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -28,6 +28,47 @@
         "js-tokens": "^4.0.0"
       }
     },
+    "@fast-csv/format": {
+      "version": "4.3.5",
+      "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz",
+      "integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==",
+      "requires": {
+        "@types/node": "^14.0.1",
+        "lodash.escaperegexp": "^4.1.2",
+        "lodash.isboolean": "^3.0.3",
+        "lodash.isequal": "^4.5.0",
+        "lodash.isfunction": "^3.0.9",
+        "lodash.isnil": "^4.0.0"
+      },
+      "dependencies": {
+        "@types/node": {
+          "version": "14.17.9",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.9.tgz",
+          "integrity": "sha512-CMjgRNsks27IDwI785YMY0KLt3co/c0cQ5foxHYv/shC2w8oOnVwz5Ubq1QG5KzrcW+AXk6gzdnxIkDnTvzu3g=="
+        }
+      }
+    },
+    "@fast-csv/parse": {
+      "version": "4.3.6",
+      "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz",
+      "integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==",
+      "requires": {
+        "@types/node": "^14.0.1",
+        "lodash.escaperegexp": "^4.1.2",
+        "lodash.groupby": "^4.6.0",
+        "lodash.isfunction": "^3.0.9",
+        "lodash.isnil": "^4.0.0",
+        "lodash.isundefined": "^3.0.1",
+        "lodash.uniq": "^4.5.0"
+      },
+      "dependencies": {
+        "@types/node": {
+          "version": "14.17.9",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.9.tgz",
+          "integrity": "sha512-CMjgRNsks27IDwI785YMY0KLt3co/c0cQ5foxHYv/shC2w8oOnVwz5Ubq1QG5KzrcW+AXk6gzdnxIkDnTvzu3g=="
+        }
+      }
+    },
     "@npmcli/arborist": {
       "version": "2.8.0",
       "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.8.0.tgz",
@@ -917,6 +958,11 @@
         "assert-plus": "^1.0.0"
       }
     },
+    "dayjs": {
+      "version": "1.10.6",
+      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.6.tgz",
+      "integrity": "sha512-AztC/IOW4L1Q41A86phW5Thhcrco3xuAA+YX/BLpLWWjRcTj5TOt/QImBLmCKlrF7u7k47arTnOyL6GnbG8Hvw=="
+    },
     "debug": {
       "version": "4.3.2",
       "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
@@ -1239,6 +1285,15 @@
       "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
       "dev": true
     },
+    "fast-csv": {
+      "version": "4.3.6",
+      "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz",
+      "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==",
+      "requires": {
+        "@fast-csv/format": "4.3.5",
+        "@fast-csv/parse": "4.3.6"
+      }
+    },
     "fast-deep-equal": {
       "version": "3.1.3",
       "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -1957,6 +2012,46 @@
       "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=",
       "dev": true
     },
+    "lodash.escaperegexp": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
+      "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c="
+    },
+    "lodash.groupby": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz",
+      "integrity": "sha1-Cwih3PaDl8OXhVwyOXg4Mt90A9E="
+    },
+    "lodash.isboolean": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+      "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
+    },
+    "lodash.isequal": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+      "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+    },
+    "lodash.isfunction": {
+      "version": "3.0.9",
+      "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz",
+      "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw=="
+    },
+    "lodash.isnil": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz",
+      "integrity": "sha1-SeKM1VkBNFjIFMVHnTxmOiG/qmw="
+    },
+    "lodash.isundefined": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz",
+      "integrity": "sha1-I+89lTVWUgOmbO/VuDD4SJEa+0g="
+    },
+    "lodash.uniq": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+      "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
+    },
     "lowercase-keys": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
@@ -2079,8 +2174,7 @@
     "minimist": {
       "version": "1.2.5",
       "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
-      "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
-      "dev": true
+      "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
     },
     "minipass": {
       "version": "3.1.3",
diff --git a/package.json b/package.json
index dead576..b482b95 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,9 @@
     "snowpack": "^3.3.7"
   },
   "dependencies": {
+    "dayjs": "^1.10.6",
+    "fast-csv": "^4.3.6",
+    "minimist": "^1.2.5",
     "purecss": "^2.0.6",
     "svelte": "^3.42.1"
   }
diff --git a/scan-csv.js b/scan-csv.js
new file mode 100644
index 0000000..eea0e1e
--- /dev/null
+++ b/scan-csv.js
@@ -0,0 +1,94 @@
+const args = require("minimist")(process.argv.slice(2));
+const fs = require("fs");
+const path = require("path");
+const csv = require("fast-csv");
+
+const weathers = {
+  pagos: [0, 4, 7, 3, 5, 8],
+  pyros: [0, 4, 7, 3, 5, 9],
+  hydatos: [0, 1, 7, 5, 6],
+};
+
+const file = args._[1];
+const zone = args._[0];
+parse(file, zone);
+
+async function parse(fpath, zone) {
+  const res = [];
+  fs.createReadStream(path.resolve(__dirname, fpath))
+    .pipe(csv.parse())
+    .on("data", (row) => {
+      res.push({
+        level: Number(row[0]),
+        name: getName(row[1]),
+        type: getType(row[1]),
+        elem: getElem(row[1]),
+        conditions: getConditions(row.slice(2, 8), zone),
+      });
+    })
+    .on("end", () => {
+      fs.writeFileSync(
+        path.resolve(__dirname, `src/${zone}.bestiary.json`),
+        JSON.stringify(res),
+        { encoding: "utf8" }
+      );
+      console.log("Done!");
+    });
+}
+
+function getName(nameStr) {
+  return /^([\w\s]+)/i.exec(nameStr)[1];
+}
+
+function getType(nameStr) {
+  if (/\(M\)/i.test(nameStr)) return 1;
+  if (/\(A\)/i.test(nameStr)) return 2;
+  return 0;
+}
+
+function getElem(nameStr) {
+  let res = /\/(\w)/i.exec(nameStr);
+  if (res) {
+    switch (res[1]) {
+      case "W":
+        return "Wind";
+      case "E":
+        return "Thunder";
+      case "H":
+        return "Water";
+      case "F":
+        return "Fire";
+      case "I":
+        return "Ice";
+      case "G":
+        return "Earth";
+    }
+  }
+  return null;
+}
+
+function getConditions(arr, zone) {
+  const res = {
+    day: [],
+    night: [],
+  };
+
+  arr.forEach((el, i) => {
+    if (el) {
+      switch (el) {
+        case "D":
+          res.day.push(weathers[zone][i]);
+          break;
+        case "N":
+          res.night.push(weathers[zone][i]);
+          break;
+        case "A":
+          res.day.push(weathers[zone][i]);
+          res.night.push(weathers[zone][i]);
+          break;
+      }
+    }
+  });
+
+  return res;
+}
diff --git a/src/App.svelte b/src/App.svelte
index 27f7f73..0de1e0e 100644
--- a/src/App.svelte
+++ b/src/App.svelte
@@ -1,17 +1,43 @@
 <script>
-  import { level } from "./stores";
+  import { level, paWeather } from "./stores";
+  import ew from "./ew";
+  import { onMount } from "svelte";
+  import { formatUtc } from "./times";
+  import { getMatches } from "./bestiary";
+
+  onMount(async () => {
+    updateWeatherStores();
+  });
+
+  let date = new Date().getTime() * (1440 / 70);
+
+  setInterval(() => {
+    date = new Date().getTime() * (1440 / 70);
+  }, 1000);
+  $: currentEzTime = formatUtc(date);
+
+  function updateWeatherStores() {
+    paWeather.set(ew.forecast(ew.PAGOS_WEATHER));
+  }
+
+  const pagosForecast = ew.forecast(ew.PAGOS_WEATHER);
+  getMatches(pagosForecast, $level);
 
   function levelChanged(evt) {
-    level.set(Number(evt.data));
+    level.set(Number(evt.target.value));
   }
 </script>
 
 <div class="app">
   <h2>
-    Level: <input on:input={levelChanged} type="number" min="1" max="60" />
+    Level: <input on:change={levelChanged} type="number" min="1" max="60" />
   </h2>
 
-  Level is {$level}
+  Level is {$level}<br />
+
+  Time is {currentEzTime}<br />
+
+  Anemos Weather is {pagosForecast[0].weatherName}
 </div>
 
 <style></style>
diff --git a/src/bestiary.js b/src/bestiary.js
new file mode 100644
index 0000000..33779d3
--- /dev/null
+++ b/src/bestiary.js
@@ -0,0 +1,47 @@
+import pagosB from "./pagos.bestiary.json";
+import day from "dayjs";
+import isBetween from "dayjs/plugin/isBetween";
+day.extend(isBetween);
+
+export function getMatches(forecast, level) {
+  console.log(level);
+  let res = [];
+  pagosB.forEach((b) => {
+    if (b.level >= level && b.level - 2 < level) {
+      res.push({
+        name: b.name,
+        elem: b.elem,
+        special: b.type > 0,
+        mutating: b.type === 1,
+        augmenting: b.type === 2,
+        uptime: findForecastMatch(forecast, b.conditions),
+      });
+    }
+  });
+  console.log(res);
+}
+
+function getEzTime() {
+  return day(new Date().getTime() * (1440 / 70));
+}
+
+function findForecastMatch(forecast, conditions) {
+  const time = getEzTime();
+  const dn = time.isBetween(time.hour(8), time.hour(18)) ? "day" : "night";
+  return {
+    isUp: forecastMatches(forecast, conditions[dn], 0).length > 0,
+    weathers: forecastMatches(forecast, conditions[dn], 0),
+    futureUptime: [1, 2, 3, 4].map((i) => {
+      const match = forecastMatches(forecast, conditions[dn], i);
+      return {
+        isUp: match.length > 0,
+        weathers: match,
+      };
+    }),
+  };
+}
+
+function forecastMatches(forecast, condition, index) {
+  const currWeather = forecast[index].currWeather;
+  return condition.filter((c) => c === currWeather);
+}
diff --git a/src/ew.js b/src/ew.js
new file mode 100644
index 0000000..3519be1
--- /dev/null
+++ b/src/ew.js
@@ -0,0 +1,141 @@
+import { aWeather, paWeather, pyWeather, hWeather } from "./stores";
+
+const WEATHER = {
+  FAIR: 0,
+  SHOWERS: 1,
+  GALES: 2,
+  BLIZZARDS: 3,
+  HEAT: 4,
+  THUNDER: 5,
+  GLOOM: 6,
+  SNOW: 7,
+  FOG: 8,
+  UMBRAL_WIND: 9,
+};
+
+// Anemos: 30/Fair, 30/Gales, 30/Showers, 10/Snowy
+const ANEMOS_WEATHER = [
+  [WEATHER.FAIR, 30],
+  [WEATHER.GALES, 30],
+  [WEATHER.SHOWERS, 30],
+  [WEATHER.SNOW, 10],
+];
+
+// Pagos: 10/Fair, 18/Fog, 18/Heat, 18/Snow, 18/Thunder, 18/Blizzards
+const PAGOS_WEATHER = [
+  [WEATHER.FAIR, 10],
+  [WEATHER.FOG, 18],
+  [WEATHER.HEAT, 18],
+  [WEATHER.SNOW, 18],
+  [WEATHER.THUNDER, 18],
+  [WEATHER.BLIZZARDS, 18],
+];
+
+// Pyros: 10/Fair, 18/Heat, 18/Thunder, 18/Blizzards, 18/Umbral Wind, 18/Snow
+const PYROS_WEATHER = [
+  [WEATHER.FAIR, 10],
+  [WEATHER.HEAT, 18],
+  [WEATHER.THUNDER, 18],
+  [WEATHER.BLIZZARDS, 18],
+  [WEATHER.UMBRAL_WIND, 18],
+  [WEATHER.SNOW, 18],
+];
+
+// Hydatos: 12/Fair, 22/Showers, 22/Gloom, 22/Thunder, 22/Snow
+const HYDATOS_WEATHER = [
+  [WEATHER.FAIR, 12],
+  [WEATHER.SHOWERS, 22],
+  [WEATHER.GLOOM, 22],
+  [WEATHER.THUNDER, 22],
+  [WEATHER.SNOW, 22],
+];
+
+function getWeatherName(weather) {
+  switch (weather) {
+    case 0:
+      return "Fair";
+    case 1:
+      return "Showers";
+    case 2:
+      return "Gales";
+    case 3:
+      return "Blizzards";
+    case 4:
+      return "Heat";
+    case 5:
+      return "Thunder";
+    case 6:
+      return "Gloom";
+    case 7:
+      return "Snow";
+    case 8:
+      return "Fog";
+    case 9:
+      return "Umbral Wind";
+    default:
+      return "Unknown";
+  }
+}
+
+function getSeed(date = new Date()) {
+  return Math.floor(date.getTime() / 1400000);
+}
+
+function hash(seed = getSeed()) {
+  const base = Math.floor(seed / 3) * 100 + ((seed + 1) % 3) * 8;
+  const step1 = ((base << 11) ^ base) >>> 0;
+  const step2 = ((step1 >>> 8) ^ step1) >>> 0;
+  return step2 % 100;
+}
+
+function hashSeq(seed = getSeed(), count = 10) {
+  const hashes = [];
+  for (let i = 0; i < count; ++i) {
+    hashes.push(hash(seed + i));
+  }
+  return hashes;
+}
+
+function getWeather(rates, hash = hashSeed()) {
+  let total = 0;
+  for (const [weather, chance] of rates) {
+    if ((total += chance) > hash) {
+      return weather;
+    }
+  }
+  return WEATHER.FAIR;
+}
+
+function forecast(rates, seed = getSeed(), count = 10) {
+  const res = [];
+  let prevHash = hash(seed - 1);
+  let prevWeather = getWeather(rates, prevHash);
+
+  for (let i = 0; res.length < count && i < 100000; ++i) {
+    const currHash = hash(seed);
+    const currWeather = getWeather(rates, currHash);
+    res.push({
+      prevWeather,
+      currWeather,
+      weatherName: getWeatherName(currWeather),
+      seed,
+      date: new Date(seed * 1400000),
+    });
+    prevHash = currHash;
+    prevWeather = currWeather;
+    ++seed;
+  }
+
+  return res;
+}
+
+export default {
+  WEATHER,
+  ANEMOS_WEATHER,
+  PAGOS_WEATHER,
+  PYROS_WEATHER,
+  HYDATOS_WEATHER,
+  getWeather,
+  forecast,
+  getWeatherName,
+};
diff --git a/src/pagos.bestiary.json b/src/pagos.bestiary.json
new file mode 100644
index 0000000..eca9afa
--- /dev/null
+++ b/src/pagos.bestiary.json
@@ -0,0 +1 @@
+[{"level":20,"name":"Sylvestre","type":2,"elem":"Wind","conditions":{"day":[0,4,7,5],"night":[3,5,8]}},{"level":20,"name":"Pagos Deepeye","type":1,"elem":"Thunder","conditions":{"day":[],"night":[0,4,7,3,5,8]}},{"level":21,"name":"Val Galago","type":1,"elem":"Thunder","conditions":{"day":[0,4,7,3,5],"night":[]}},{"level":21,"name":"Deadly Dodo","type":0,"elem":null,"conditions":{"day":[],"night":[]}},{"level":21,"name":"Northern Fluturini","type":2,"elem":"Wind","conditions":{"day":[8],"night":[5,8]}},{"level":22,"name":"Snow Slug","type":2,"elem":"Water","conditions":{"day":[3],"night":[3,5]}},{"level":22,"name":"Val Banemite","type":1,"elem":"Thunder","conditions":{"day":[4,3,5],"night":[0,5,8]}},{"level":22,"name":"Val Roselet","type":0,"elem":null,"conditions":{"day":[],"night":[]}},{"level":null,"name":"Zombie Brobinyak","type":2,"elem":"Earth","conditions":{"day":[],"night":[8]}},{"level":23,"name":"Val Mole","type":1,"elem":"Earth","conditions":{"day":[0],"night":[0]}},{"level":23,"name":"Val Ant","type":0,"elem":null,"conditions":{"day":[],"night":[]}},{"level":23,"name":"Iceskin Peiste","type":0,"elem":null,"conditions":{"day":[],"night":[]}},{"level":24,"name":"Snow Aurelia","type":2,"elem":"Water","conditions":{"day":[0,7,5],"night":[0,3,8]}},{"level":24,"name":"Pagos Wolf","type":1,"elem":"Ice","conditions":{"day":[],"night":[0,4,7,3,5,8]}},{"level":24,"name":"Yarzon Survivor","type":0,"elem":null,"conditions":{"day":[],"night":[]}},{"level":25,"name":"Pagos Puk","type":1,"elem":"Fire","conditions":{"day":[5],"night":[5]}},{"level":25,"name":"Savage Ruszor","type":0,"elem":null,"conditions":{"day":[],"night":[]}},{"level":25,"name":"Thunderstorm Sprite","type":1,"elem":"Thunder","conditions":{"day":[5],"night":[5]}},{"level":null,"name":"Demon of the Incunable","type":2,"elem":"Wind","conditions":{"day":[],"night":[4,7,3,5,8]}},{"level":26,"name":"Val Mantis","type":2,"elem":"Earth","conditions":{"day":[4,3],"night":[4,7,3,5,8]}},{"level":26,"name":"Pagos Tursus","type":2,"elem":"Ice","conditions":{"day":[],"night":[]}},{"level":26,"name":"Snowmelt Sprite","type":1,"elem":"Water","conditions":{"day":[8],"night":[8]}},{"level":26,"name":"Northern Colibri","type":1,"elem":"Wind","conditions":{"day":[4,7,3,5,8],"night":[]}},{"level":27,"name":"Pagos Bear","type":1,"elem":"Earth","conditions":{"day":[7,3],"night":[7,3]}},{"level":27,"name":"Frozen Gelato","type":2,"elem":"Ice","conditions":{"day":[0,3,5],"night":[0,4,7,3,5,8]}},{"level":27,"name":"Ember Sprite","type":1,"elem":"Fire","conditions":{"day":[4],"night":[4]}},{"level":28,"name":"Coralline Uragnite","type":1,"elem":"Water","conditions":{"day":[4,3,5],"night":[4,7,8]}},{"level":28,"name":"Eurekan Vindthurs","type":1,"elem":"Ice","conditions":{"day":[4],"night":[4]}},{"level":29,"name":"Pagos Wildebeest","type":1,"elem":"Thunder","conditions":{"day":[0,4,7,3,5,8],"night":[4,7,5,8]}},{"level":29,"name":"Emberflash Matamata","type":1,"elem":"Water","conditions":{"day":[4],"night":[4]}},{"level":29,"name":"Zombie Wyvern","type":2,"elem":"Wind","conditions":{"day":[4,7,3,5,8],"night":[4,7,3,8]}},{"level":30,"name":"Ice Mirrorknight","type":1,"elem":"Wind","conditions":{"day":[4],"night":[4]}},{"level":30,"name":"Pagos Croc","type":1,"elem":"Earth","conditions":{"day":[],"night":[0,4,7,3,5,8]}},{"level":null,"name":"Geshunpest","type":2,"elem":"Thunder","conditions":{"day":[],"night":[7,3,8]}},{"level":31,"name":"Withered Sankchinni","type":1,"elem":"Wind","conditions":{"day":[8],"night":[8]}},{"level":31,"name":"Val Guardian","type":1,"elem":"Earth","conditions":{"day":[],"night":[0,4,7,3,5,8]}},{"level":32,"name":"Pagos Hippocerf","type":2,"elem":"Wind","conditions":{"day":[4,7,3,5],"night":[7,3,5,8]}},{"level":32,"name":"Decotitus","type":1,"elem":"Earth","conditions":{"day":[0,4,7,3,5,8],"night":[]}},{"level":32,"name":"Snowstorm Sprite","type":1,"elem":"Ice","conditions":{"day":[7,3],"night":[7,3]}},{"level":33,"name":"Pagos Centaur","type":1,"elem":"Thunder","conditions":{"day":[4],"night":[4]}},{"level":33,"name":"Val Keeper","type":2,"elem":"Earth","conditions":{"day":[4,7,3,5,8],"night":[7,3,5,8]}},{"level":33,"name":"Thunderstorm Sprite","type":1,"elem":"Thunder","conditions":{"day":[5],"night":[5]}},{"level":null,"name":"Haunt","type":2,"elem":"Water","conditions":{"day":[],"night":[0,4,7,3,5,8]}},{"level":34,"name":"Urolith Guard","type":1,"elem":"Earth","conditions":{"day":[7,3],"night":[7,3]}},{"level":34,"name":"Frozen Aevis","type":1,"elem":"Thunder","conditions":{"day":[4,7,3,5,8],"night":[]}},{"level":35,"name":"Valley Manticore","type":1,"elem":"Fire","conditions":{"day":[0,4,7,3,5,8],"night":[]}},{"level":35,"name":"Val Corpse Flower","type":1,"elem":"Ice","conditions":{"day":[0],"night":[0]}},{"level":36,"name":"Blizzard Goobbue","type":1,"elem":"Ice","conditions":{"day":[],"night":[0,4,7,3,5,8]}},{"level":null,"name":"Val Corpse","type":2,"elem":"Ice","conditions":{"day":[],"night":[0,7,8]}},{"level":36,"name":"World Serpent","type":1,"elem":"Fire","conditions":{"day":[8],"night":[8]}},{"level":37,"name":"Storm Manta","type":1,"elem":"Water","conditions":{"day":[0],"night":[0]}},{"level":37,"name":"Void Syricta","type":1,"elem":"Earth","conditions":{"day":[3],"night":[3]}},{"level":38,"name":"Val Ymir","type":1,"elem":"Thunder","conditions":{"day":[4,8],"night":[]}},{"level":38,"name":"Pagos Anubys","type":1,"elem":"Water","conditions":{"day":[0,4,7,3,5,8],"night":[0,4,7,3,5,8]}},{"level":39,"name":"Greater Amphiphtre","type":1,"elem":"Fire","conditions":{"day":[0,5],"night":[0,5]}},{"level":39,"name":"Escaped Tyrannosaur","type":1,"elem":"Earth","conditions":{"day":[],"night":[0,4,7,3,5,8]}},{"level":40,"name":"Frozen Void Dragon","type":2,"elem":"Ice","conditions":{"day":[0,4,7,3,5,8],"night":[0,4,3,5,8]}},{"level":40,"name":"Pagos Chimera","type":1,"elem":"Thunder","conditions":{"day":[3],"night":[3]}},{"level":40,"name":"Val Griffin","type":1,"elem":"Wind","conditions":{"day":[0],"night":[0]}}]
\ No newline at end of file
diff --git a/src/stores.js b/src/stores.js
index f203bfd..65b3a79 100644
--- a/src/stores.js
+++ b/src/stores.js
@@ -3,6 +3,10 @@ import { writable } from "svelte/store";
 const localStorage = window.localStorage;
 
 export const level = localStorageStore(0);
+export const aWeather = writable();
+export const paWeather = writable();
+export const pyWeather = writable();
+export const hWeather = writable();
 
 function localStorageStore(key) {
   const item = localStorage.getItem(key);
diff --git a/src/times.js b/src/times.js
new file mode 100644
index 0000000..5615c19
--- /dev/null
+++ b/src/times.js
@@ -0,0 +1,7 @@
+import day from "dayjs";
+import utc from "dayjs/plugin/utc";
+day.extend(utc);
+
+export function formatUtc(date) {
+  return day.utc(date).format("HH:mm");
+}