Show the original ability/price for SplatNet gear

Data is sourced from Inkipedia.
This commit is contained in:
Unknown 2017-10-15 14:08:43 -07:00
parent d389708bfa
commit 5d6c5b45bb
11 changed files with 1884 additions and 23 deletions

View File

@ -31,6 +31,7 @@
"html-loader": "^0.5.0",
"html-webpack-plugin": "^2.30.1",
"ical.js": "^1.2.2",
"json-stable-stringify": "^1.0.1",
"make-runnable": "^1.3.6",
"mkdirp": "^0.5.1",
"moment-timezone": "^0.5.13",

View File

@ -317,6 +317,15 @@ body.has-modal #main {
border-radius: 15px;
padding: 38px 10px 0 10px;
transition: transform 0.2s ease;
&.tilt-left:hover {
transform: rotate(-1.4deg) scale(1.05);
}
&.tilt-right:hover {
transform: rotate(1.4deg) scale(1.05);
}
.brand {
position: absolute;
top: 6px;
@ -358,36 +367,75 @@ body.has-modal #main {
.gear-image {
margin: 0 20px;
transition: transform 0.2s ease;
}
&:hover .gear-image {
transform: scale(1.1);
}
.gear-name {
position: relative;
}
.frequent-skill {
.info-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: rgba($white-ter, 0.9);
border-radius: 10px;
opacity: 0;
transition: opacity 0.2s ease;
transform: translate(0, 2px);
transition: all 0.2s ease;
.info-overlay-container {
margin-top: 2px;
background: rgba($white-ter, 0.9);
border-radius: 10px;
}
.skill-img-bg img {
width: 26px;
}
.original-gear {
.skill-img-bg img {
width: 24px;
}
.sub {
margin: 7px 0 0 2px;
img {
width: 15px;
}
}
.cash {
width: 20px;
height: 20px;
}
.strikethrough {
position: relative;
}
.strikethrough:before {
position: absolute;
content: "";
left: -5px;
top: 50%;
right: -5px;
border-top: 3px solid;
border-color: red;
opacity: 0.6;
-webkit-transform:rotate(-20deg);
-moz-transform:rotate(-20deg);
-ms-transform:rotate(-20deg);
-o-transform:rotate(-20deg);
transform:rotate(-20deg);
}
}
}
&:hover .frequent-skill {
&:hover .info-overlay {
opacity: 1;
transform: none;
}
.bottom-bar {
@ -399,12 +447,12 @@ body.has-modal #main {
color: $white-ter;
@extend .text-shadow;
}
.cash {
width: 24px;
height: 24px;
vertical-align: text-bottom;
}
.cash {
width: 24px;
height: 24px;
vertical-align: text-bottom;
}
&.shoes .bottom-bar {

View File

@ -18,7 +18,11 @@
<p>
This site was built with <a href="https://vuejs.org/" target="_blank">Vue.js</a>
and <a href="http://bulma.io/" target="_blank">Bulma</a>.
All data comes from the SplatNet 2 API.
Data comes from the SplatNet 2 API and other manually-maintained sources.
</p>
<p>
Original SplatNet gear ability/price data comes from
<a href="https://splatoonwiki.org/wiki/Gear#In_Splatoon_2" target="_blank">Inkipedia</a>.
</p>
<p>
Source code for this site is available on

View File

@ -28,13 +28,40 @@
<div class="gear-name has-text-centered">
{{ merchandise.gear.name }}
<div class="frequent-skill">
<div class="is-size-7">{{ merchandise.gear.brand.name }}</div>
<div>
<div class="skill-img-bg">
<img :src="merchandise.gear.brand.frequent_skill.image | localSplatNetImageUrl" :title="merchandise.gear.brand.frequent_skill.name" />
<div class="info-overlay">
<div class="info-overlay-container original-gear" v-if="originalGear">
<div class="is-size-7">Original Gear</div>
<div class="level" style="margin: 0 3px">
<div class="level-left">
<div class="level-item">
<div class="skill-img-bg strikethrough">
<img :src="originalGear.skill.image | localSplatNetImageUrl" :title="originalGear.skill.name" />
</div>
<div class="sub" v-for="i in originalGear.rarity + 1">
<img src="~@/img/blank-skill-slot.png" />
</div>
</div>
</div>
<div class="level-right">
<div class="level-item">
<div>
<img class="cash" src="~@/img/cash.png" />
<span class="strikethrough">{{ originalGear.price }}</span>
</div>
</div>
</div>
</div>
</div>
<div class="info-overlay-container">
<div class="is-size-7">{{ merchandise.gear.brand.name }}</div>
<div>
<div class="skill-img-bg">
<img :src="merchandise.gear.brand.frequent_skill.image | localSplatNetImageUrl" :title="merchandise.gear.brand.frequent_skill.name" />
</div>
Common Ability
</div>
Common Ability
</div>
</div>
</div>
@ -47,7 +74,14 @@
</template>
<script>
import { getOriginalGear } from '@/js/splatoon';
export default {
props: ['merchandise', 'now'],
computed: {
originalGear() {
return getOriginalGear(this.merchandise.gear);
},
},
}
</script>

107
src/js/data/brands.json Normal file
View File

@ -0,0 +1,107 @@
{
"0": {
"frequent_skill": "11",
"id": "0",
"image": "/images/brand/5547e529d160b188d104e3b68ff4b7566eab9771.png",
"name": "SquidForce"
},
"1": {
"frequent_skill": "9",
"id": "1",
"image": "/images/brand/3d4661b3f60f4b74e9bb6760ba7397ecd2502a20.png",
"name": "Zink"
},
"10": {
"frequent_skill": "2",
"id": "10",
"image": "/images/brand/02286fe17bb6bc3f5f13c6b251ddc0a55c44c756.png",
"name": "Tentatek"
},
"11": {
"frequent_skill": "5",
"id": "11",
"image": "/images/brand/eab05d2d502cf953b4ae034c87e52e8c999339d6.png",
"name": "Takoroka"
},
"15": {
"frequent_skill": "13",
"id": "15",
"image": "/images/brand/0b09659e2770389dcb23d911359175e8686f85f5.png",
"name": "Annaki"
},
"16": {
"frequent_skill": "10",
"id": "16",
"image": "/images/brand/de96243d58e41e928d30290162a6f496033da868.png",
"name": "Enperry"
},
"17": {
"frequent_skill": "13",
"id": "17",
"image": "/images/brand/4b05e494bf9a547b4d625fd52dcdd930a6c4defc.png",
"name": "Toni Kensa"
},
"2": {
"frequent_skill": "4",
"id": "2",
"image": "/images/brand/9ac5752790dd6dbdc7a427df95e1bfe89fe318e0.png",
"name": "Krak-On"
},
"3": {
"frequent_skill": "3",
"id": "3",
"image": "/images/brand/451a2d0b5ceb7ea4ec4e47c3ff05eee362e9b722.png",
"name": "Rockenberg"
},
"4": {
"frequent_skill": "6",
"id": "4",
"image": "/images/brand/f3d01187fd633e7d48d9e4e16ef31da73279293c.png",
"name": "Zekko"
},
"5": {
"frequent_skill": "7",
"id": "5",
"image": "/images/brand/b38b99b3358f587efd1613b72a72c9ca9f81f406.png",
"name": "Forge"
},
"6": {
"frequent_skill": "1",
"id": "6",
"image": "/images/brand/dee59c0797a6214114e527dfa51f0dd012085172.png",
"name": "Firefin"
},
"7": {
"frequent_skill": "8",
"id": "7",
"image": "/images/brand/8175954b5a7e02b8097dbb484c808c8f39d31f41.png",
"name": "Skalop"
},
"8": {
"frequent_skill": "0",
"id": "8",
"image": "/images/brand/36bc10db0aa2640c87ad712fc0281515beedcca1.png",
"name": "Splash Mob"
},
"9": {
"frequent_skill": "12",
"id": "9",
"image": "/images/brand/80648c6e427eae4d32677797fa1be7c0e253fda5.png",
"name": "Inkline"
},
"97": {
"id": "97",
"image": "/images/brand/ec85f17c315e0a1ea4dee55041fd30a88d6aba93.png",
"name": "Grizzco"
},
"98": {
"id": "98",
"image": "/images/brand/047cbc2f0674eeb4796efb3b6ec1b710b22d07e7.png",
"name": "Cuttlegear"
},
"99": {
"id": "99",
"image": "/images/brand/154440ecbfb65a22cdb224fac843b02cccbcad03.png",
"name": "amiibo"
}
}

File diff suppressed because it is too large Load Diff

132
src/js/data/skills.json Normal file
View File

@ -0,0 +1,132 @@
{
"0": {
"id": "0",
"image": "/images/skill/04b1de71fba1f14197b9163503955c52fd74858b.png",
"name": "Ink Saver (Main)"
},
"1": {
"id": "1",
"image": "/images/skill/da8ff08954fd5d890fc8bc4dd4cb761e2a33b703.png",
"name": "Ink Saver (Sub)"
},
"10": {
"id": "10",
"image": "/images/skill/34e114a50a001778a574f7061039d43e632137b7.png",
"name": "Sub Power Up"
},
"100": {
"id": "100",
"image": "/images/skill/492691ed2a7ce7ec694bf88d3ea1f692b7da1692.png",
"name": "Opening Gambit"
},
"101": {
"id": "101",
"image": "/images/skill/03d1b1e0221fc82d0ab26cb8cd3f5f40107b6190.png",
"name": "Last-Ditch Effort"
},
"102": {
"id": "102",
"image": "/images/skill/d59c8affb196bc3dcb6faea34899e782bce37656.png",
"name": "Tenacity"
},
"103": {
"id": "103",
"image": "/images/skill/bdc5135874439cf3169d9a54b3f1fbdba3731b34.png",
"name": "Comeback"
},
"104": {
"id": "104",
"image": "/images/skill/f0a99d1ab1a765b992b79610ebdc25b69d88fae9.png",
"name": "Ninja Squid"
},
"105": {
"id": "105",
"image": "/images/skill/53c62995f9d2dc4a60f3850c5dbdd2323f1eef87.png",
"name": "Haunt"
},
"106": {
"id": "106",
"image": "/images/skill/1d6ed2ef9cd1f71086368e9ec4b5704663ba3a0a.png",
"name": "Thermal Ink"
},
"107": {
"id": "107",
"image": "/images/skill/344e4c16ad4ec9ab8f56711d8d79a1ffb9228a1a.png",
"name": "Respawn Punisher"
},
"108": {
"id": "108",
"image": "/images/skill/67e4e8ec069dfaec1d732c7fe407a2c73e8e51b8.png",
"name": "Ability Doubler"
},
"109": {
"id": "109",
"image": "/images/skill/8a3f06a972689b094f762626ff36b3db8ee545b5.png",
"name": "Stealth Jump"
},
"11": {
"id": "11",
"image": "/images/skill/33087a476135074af856151a89a6fe4d1d3a996e.png",
"name": "Ink Resistance Up"
},
"110": {
"id": "110",
"image": "/images/skill/47a74cd575b25a9de3e3592084ff3870db0cf4e0.png",
"name": "Object Shredder"
},
"111": {
"id": "111",
"image": "/images/skill/d0de52e89947803e5b24165335855f39f9e8a6bd.png",
"name": "Drop Roller"
},
"12": {
"id": "12",
"image": "/images/skill/35f4ec4284fc5a19da58ffb1a7988eb26eb8bd7f.png",
"name": "Bomb Defense Up"
},
"13": {
"id": "13",
"image": "/images/skill/efa003501e1152ef7b617b9e01517c915e05b7ac.png",
"name": "Cold-Blooded"
},
"2": {
"id": "2",
"image": "/images/skill/c14f4471b26e0f918c736b5c17e03212290f4541.png",
"name": "Ink Recovery Up"
},
"3": {
"id": "3",
"image": "/images/skill/7de1bdfd875ef470b6066c17bed726b5b5113d48.png",
"name": "Run Speed Up"
},
"4": {
"id": "4",
"image": "/images/skill/d138c293c8ddac42fadf0e6531100a88c79c81f6.png",
"name": "Swim Speed Up"
},
"5": {
"id": "5",
"image": "/images/skill/1378f3963526d7216ec44da35b924b81a8ff6a37.png",
"name": "Special Charge Up"
},
"6": {
"id": "6",
"image": "/images/skill/d83b962b84fcea9d02c591c296234f5de77f9682.png",
"name": "Special Saver"
},
"7": {
"id": "7",
"image": "/images/skill/f20a3e85feeb6b4bb021d28059afd6265cee0b43.png",
"name": "Special Power Up"
},
"8": {
"id": "8",
"image": "/images/skill/84ab4ba1188849dff63a4314955a53ab103b47df.png",
"name": "Quick Respawn"
},
"9": {
"id": "9",
"image": "/images/skill/daf6883039afa62da91eb93eb2a40b673f10b715.png",
"name": "Quick Super Jump"
}
}

18
src/js/splatoon.js Normal file
View File

@ -0,0 +1,18 @@
const skills = require('./data/skills');
const inkipediaGear = require('./data/inkipediaGear');
function getOriginalGear(gear) {
if (!gear)
return;
let originalGear = inkipediaGear[gear.kind].find(ig => ig.name == gear.name);
if (!originalGear)
return;
return Object.assign({}, originalGear, { skill: skills[originalGear.skill] });
}
module.exports = {
getOriginalGear,
}

View File

@ -0,0 +1,49 @@
const delay = require('delay');
const splatnet = require('./splatnet');
/**
* We can't retrieve gear/brand/skill data directly.
* This pulls data from the 50 most recent battles instead.
*/
module.exports = async function retrieveGearData() {
let skills = {};
let brands = {};
let gear = { head: {}, clothes: {}, shoes: {} };
console.info('Getting battle data...');
let battleResultsData = await splatnet.getResults();
for (let battle of battleResultsData.results) {
await delay(250);
console.info(`Getting data for battle #${battle.battle_number}...`);
let battleData = await splatnet.getResults(battle.battle_number);
// Find all gear and skills
let players = [battleData.player_result].concat(battleData.my_team_members, battleData.other_team_members);
for (player of players) {
// Get the main and sub skills for each type of gear
let gearSkills = [
player.player.head_skills.main,
player.player.clothes_skills.main,
player.player.shoes_skills.main,
].concat(player.player.head_skills.subs, player.player.clothes_skills.subs, player.player.shoes_skills.subs);
for (skill of gearSkills) {
if (skill)
skills[skill.id] = skill;
}
// Get brands
for (brand of [player.player.head.brand, player.player.clothes.brand, player.player.shoes.brand])
brands[brand.id] = brand;
// Get gear
gear.head[player.player.head.id] = player.player.head;
gear.clothes[player.player.clothes.id] = player.player.clothes;
gear.shoes[player.player.shoes.id] = player.player.shoes;
}
}
return { skills, brands, gear };
}

View File

@ -72,6 +72,12 @@ async function getLeagueMatchRanking(year, month, day, hour, type = 'T', region
return response.data;
}
async function getResults(id = null) {
let url = (id) ? `results/${id}` : 'results';
let response = await api.get(url);
return response.data;
}
async function getImage(imagePath) {
let response = await axios.get(`${splatnetBaseUrl}${imagePath}`, { responseType: 'arraybuffer', headers: { 'User-Agent': userAgent } });
return response.data;
@ -85,5 +91,6 @@ module.exports = {
getJPFestivals,
getMerchandises,
getLeagueMatchRanking,
getResults,
getImage,
}

95
src/updater/updateGear.js Normal file
View File

@ -0,0 +1,95 @@
// Note: this script is intended to be run during development (not in production).
// It updates the gear/brand/skill data located at /src/js/data/.
require('./bootstrap');
const path = require('path');
const fs = require('fs');
const mkdirp = require('mkdirp');
const axios = require('axios');
const retrieveGearData = require('./retrieveGearData');
const stringify = require('json-stable-stringify');
const dataPath = path.resolve('src/js/data');
const brandsFilename = `${dataPath}/brands.json`;
const skillsFilename = `${dataPath}/skills.json`;
const inkipediaGearFilename = `${dataPath}/inkipediaGear.json`;
mkdirp(dataPath);
function applyData(oldData, newData) {
if (!Array.isArray(newData))
newData = Object.values(newData);
for (let data of newData) {
if (data.id in oldData)
Object.assign(oldData[data.id], data);
else
oldData[data.id] = data;
}
}
(async () => {
let brands = {};
let skills = {};
// Retrieve existing gear/brand/skill data if it exists
if (fs.existsSync(brandsFilename))
brands = JSON.parse(fs.readFileSync(brandsFilename));
if (fs.existsSync(skillsFilename))
skills = JSON.parse(fs.readFileSync(skillsFilename));
// Update gear/brand/skill data from SplatNet
// (This takes a while and doesn't need to be updated frequently, so just disabling this here for now)
if (false) {
let gearData = await retrieveGearData();
applyData(brands, gearData.brands);
applyData(skills, gearData.skills);
}
// Process brands
for (let brand of Object.values(brands)) {
if (brand.frequent_skill && typeof brand.frequent_skill === 'object')
brand.frequent_skill = brand.frequent_skill.id;
}
// Retrieve data from Inkipedia
let inkipediaGear = {};
let inkipediaSources = {
head: 'https://splatoonwiki.org/w/index.php?title=Template:Gear/S2_Headgear&action=edit',
clothes: 'https://splatoonwiki.org/w/index.php?title=Template:Gear/S2_Clothing&action=edit',
shoes: 'https://splatoonwiki.org/w/index.php?title=Template:Gear/S2_Shoes&action=edit',
}
for (let key in inkipediaSources) {
inkipediaGear[key] = [];
let response = await axios.get(inkipediaSources[key]);
let regex = /\{\{GearList\/Item.*?filter/g;
let row;
while (row = regex.exec(response.data)) {
let details = /name=(.*?)\|brand=(.*?)\|cost=(.*?)\|ability=(.*?)\|rarity=(.*?)\|/.exec(row[0]);
if (details) {
inkipediaGear[key].push({
name: details[1],
brand: details[2],
price: parseInt(details[3].replace(',', '')),
skill: details[4],
rarity: parseInt(details[5]) - 1,
});
}
}
}
// Process Inkipedia gear
for (let item of [].concat(Object.values(inkipediaGear.head), Object.values(inkipediaGear.clothes), Object.values(inkipediaGear.shoes))) {
item.brand = Object.values(brands).find(b => b.name == item.brand).id;
item.skill = Object.values(skills).find(s => s.name == item.skill).id;
}
// Write out the data
fs.writeFileSync(brandsFilename, stringify(brands, { space: 4 }));
fs.writeFileSync(skillsFilename, stringify(skills, { space: 4 }));
fs.writeFileSync(inkipediaGearFilename, stringify(inkipediaGear, { space: 4 }));
})();