오윤석

Merge branch 'feature/2-character_page' into develop

......@@ -6,6 +6,6 @@ let routes = require('./routes');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.get('/api/home', routes.home);
app.get('/api/character', routes.character.getCharacter);
let server = app.listen(80);
\ No newline at end of file
......
This diff is collapsed. Click to expand it.
module.exports = {
'STR': {
'korean': '힘'
},
'DEX': {
'korean': '민첩성'
},
'INT': {
'korean': '지력'
},
'LUK': {
'korean': '운'
}
}
\ No newline at end of file
module.exports = {
'한손검': 1.2,
'한손도끼': 1.2,
'한손둔기': 1.2,
'스태프': 1,
'완드': 1,
'샤이닝 로드': 1.2,
'단검': 1.3,
'케인': 1.3,
'데스페라도': 1.3,
'에너지소드': 1.5,
'소울 슈터': 1.7,
'ESP 리미터': 1.2,
'체인': 1.3,
'매직 건틀렛': 1.2,
'부채': 1.3,
'튜너': 1.3,
'두손검': 1.34,
'두손도끼': 1.34,
'두손둔기': 1.34,
'창': 1.34,
'폴암': 1.49,
'태도': 1.34,
'건틀렛 리볼버': 1.7,
'활': 1.3,
'석궁': 1.35,
'듀얼 보우건': 1.3,
'에인션트 보우': 1.3,
'아대': 1.75,
'건': 1.5,
'너클': 1.7,
'핸드캐논': 1.5
}
\ No newline at end of file
This diff is collapsed. Click to expand it.
......@@ -9,8 +9,11 @@
"author": "",
"license": "ISC",
"dependencies": {
"axios": "^0.19.2",
"express": "^4.17.1",
"http": "0.0.1-security",
"jquery": "^3.5.1",
"jsdom": "^16.2.2",
"nodemon": "^2.0.4"
}
}
......
This diff is collapsed. Click to expand it.
module.exports = function(req, res) {
res.send('this is home');
}
\ No newline at end of file
let routes = {};
routes.home = require('./home');
routes.character = require('./character');
module.exports = routes;
\ No newline at end of file
......
......@@ -30,6 +30,14 @@
"js-tokens": "^4.0.0"
}
},
"@lottiefiles/svelte-lottie-player": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/@lottiefiles/svelte-lottie-player/-/svelte-lottie-player-0.1.4.tgz",
"integrity": "sha512-NVNDdB1kQLIOytWsTxEYRRHhl2T9oZFfKSkUEr8m73JM7jaLP8J8GGH9B6A0vXU4wKsjR69ET6JEvib6z1QH+g==",
"requires": {
"lottie-web": "^5.6.8"
}
},
"@nodelib/fs.scandir": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz",
......@@ -85,9 +93,9 @@
}
},
"@rollup/pluginutils": {
"version": "3.0.10",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.0.10.tgz",
"integrity": "sha512-d44M7t+PjmMrASHbhgpSbVgtL6EFyX7J4mYxwQ/c5eoaE6N2VgCgEcWVzNnwycIloti+/MpwFr8qfw+nRw00sw==",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
"integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
"dev": true,
"requires": {
"@types/estree": "0.0.39",
......@@ -101,11 +109,6 @@
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
"dev": true
},
"@types/events": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
"integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g=="
},
"@types/fs-extra": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.1.tgz",
......@@ -115,11 +118,10 @@
}
},
"@types/glob": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz",
"integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==",
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.2.tgz",
"integrity": "sha512-VgNIkxK+j7Nz5P7jvUZlRvhuPSmsEfS03b0alKcq5V/STUKAa3Plemsn5mrQUO7am6OErJ4rhGEGJbACclrtRA==",
"requires": {
"@types/events": "*",
"@types/minimatch": "*",
"@types/node": "*"
}
......@@ -130,9 +132,9 @@
"integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA=="
},
"@types/node": {
"version": "14.0.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.9.tgz",
"integrity": "sha512-0sCTiXKXELOBxvZLN4krQ0FPOAA7ij+6WwvD0k/PHd9/KAkr4dXel5J9fh6F4x1FwAQILqAWkmpeuS6mjf1iKA=="
"version": "14.0.13",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.13.tgz",
"integrity": "sha512-rouEWBImiRaSJsVA+ITTFM6ZxibuAlTuNOCyxVbwreu6k6+ujs7DfnU9o+PShFhET78pMBl3eH+AGSI5eOTkPA=="
},
"@types/resolve": {
"version": "0.0.8",
......@@ -144,9 +146,9 @@
}
},
"acorn": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz",
"integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz",
"integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==",
"dev": true
},
"ansi-styles": {
......@@ -163,6 +165,14 @@
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="
},
"axios": {
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
"integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
"requires": {
"follow-redirects": "1.5.10"
}
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
......@@ -244,6 +254,14 @@
"resolved": "https://registry.npmjs.org/console-clear/-/console-clear-1.1.1.tgz",
"integrity": "sha512-pMD+MVR538ipqkG5JXeOEbKWS5um1H4LUUccUQG68qpeqBYbzYy79Gh55jkd2TtPdRfUaLWdv6LPP//5Zt0aPQ=="
},
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
......@@ -293,6 +311,14 @@
"to-regex-range": "^5.0.1"
}
},
"follow-redirects": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
"requires": {
"debug": "=3.1.0"
}
},
"fs-extra": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
......@@ -478,6 +504,11 @@
"resolved": "https://registry.npmjs.org/local-access/-/local-access-1.0.1.tgz",
"integrity": "sha512-ykt2pgN0aqIy6KQC1CqdWTWkmUwNgaOS6dcpHVjyBJONA+Xi7AtSB1vuxC/U/0tjIP3wcRudwQk1YYzUvzk2bA=="
},
"lottie-web": {
"version": "5.6.10",
"resolved": "https://registry.npmjs.org/lottie-web/-/lottie-web-5.6.10.tgz",
"integrity": "sha512-ucTzaiBJOMm56/K7Wqjajyh7qXlonHKB3+b5fHvhSiz+jOrXt6QDNKAinI3qdw/zvvYHKzXvFMy5SgOXbOJ5ng=="
},
"magic-string": {
"version": "0.25.7",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
......@@ -525,6 +556,11 @@
"resolved": "https://registry.npmjs.org/mri/-/mri-1.1.5.tgz",
"integrity": "sha512-d2RKzMD4JNyHMbnbWnznPaa8vbdlq/4pNZ3IgdaGrVbBhebBsGUUE/6qorTMYNS6TwuH3ilfOlD2bf4Igh8CKg=="
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
......@@ -722,9 +758,9 @@
}
},
"svelte": {
"version": "3.23.0",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.23.0.tgz",
"integrity": "sha512-cnyd96bK/Nw5DnYuB1hzm5cl6+I1fpmdKOteA7KLzU9KGLsLmvWsSkSKbcntzODCLmSySN3HjcgTHRo6/rJNTw==",
"version": "3.23.2",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.23.2.tgz",
"integrity": "sha512-hE8GeTM83YVR4GY6/6PeDEcGct4JS5aCi+IYbCAa76oaPSfuF7L85DQYULQxlTK/KPWzw3L1GRGmC3oPG/PQoA==",
"dev": true
},
"svelte-spa-router": {
......
......@@ -15,6 +15,8 @@
"svelte": "^3.0.0"
},
"dependencies": {
"@lottiefiles/svelte-lottie-player": "^0.1.4",
"axios": "^0.19.2",
"rollup-plugin-copy": "^3.3.0",
"sirv-cli": "^0.4.4",
"svelte-spa-router": "^2.1.0"
......
<!DOCTYPE html>
<html lang="en">
<head>
<head lang="ko">
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>::메이플스토리 스펙 계산기::</title>
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel='stylesheet' href='/global.css'>
<link rel='icon' type='shortcut icon' href='./build/static/images/favicon.ico'>
<link rel='stylesheet' href='./static/css/global.css'>
<link rel='stylesheet' href='/build/bundle.css'>
<meta property="og:image" content="./build/static/images/ogimage.png">
<meta property="og:title" content="::메이플스토리 스펙 계산기::">
<meta property="og:description" content="당신의 메이플스토리 스펙 효율을 빠르게 계산해보세요!">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
......
......@@ -38,7 +38,7 @@ export default {
copy({
targets: [
{ src: 'src/images', dest: 'public' }
{ src: 'src/images', dest: 'public/build/static' }
]
}),
......
<script>
import {LottiePlayer} from '@lottiefiles/svelte-lottie-player';
export let count = 0;
</script>
<div id="loading" class:show={count > 0}>
<LottiePlayer
src="https://assets5.lottiefiles.com/packages/lf20_kblbXu.json" background="transparent" speed="1" width={300} height={300} controls={false} controlsLayout={null} loop autoplay>
</LottiePlayer>
</div>
<style>
#loading { position:fixed; width:100%; height:100%; z-index:9999; background-color:rgba(0,0,0,0.3); align-items:center; justify-content:center; display:none; }
#loading.show { display:flex; }
</style>
\ No newline at end of file
No preview for this file type
import Search from './routes/Search.svelte';
import Character from './routes/Character.svelte';
const routes = {
'/': Search,
'/character': Character,
'/character/:character': Character,
};
export default routes;
\ No newline at end of file
......
<script>
export let params = {};
import {push} from 'svelte-spa-router';
import axios from 'axios';
import Loading from '../components/Loading.svelte';
let callCount = 0;
let isBuff = false;
let isCharacterLoading = false;
let stats;
let character = {
info:{
avatar:'',
nickname:'',
characterCode:'',
job:'',
majorName:'',
attackPowerName:'',
server:{
name:'',
icon:''
},
level:237
},
analysis:{
default:{
stats:{
major:{
pure:0,
percent:0,
added:0,
},
minor:0,
damage:{
all:0,
boss:0
},
finalDamage:0,
criticalDamage:0,
attackPower:{
pure:0,
percent:0,
},
ignoreGuard:0
},
efficiency:{
major:{
pure:1,
percent:1
},
attackPower:{
pure:1,
percent:1
},
damage:1,
criticalDamage:1,
ignoreGuard:1
}
}
},
buff:{
stats:{
major:{
pure:0,
percent:0,
added:0,
},
minor:0,
damage:{
all:0,
boss:0
},
finalDamage:0,
criticalDamage:0,
attackPower:{
pure:0,
percent:0,
},
ignoreGuard:0
},
efficiency:{
major:{
pure:1,
percent:1
},
attackPower:{
pure:1,
percent:1
},
damage:1,
criticalDamage:1,
ignoreGuard:1
}
}
}
function init() {
let nickname;
if(!params.character) {
M.toast({html:"캐릭터명을 입력해주세요."});
push("/");
}
nickname = decodeURI(params.character);
callCount++;
axios.get('/api/character', {
params:{
nickname:nickname
}
}).then(function(response) {
character = response.data;
isCharacterLoading = true;
}).catch(function(error) {
switch(error.response.status) {
case 404:
M.toast({html:"존재하지 않는 캐릭터입니다."});
break;
case 403:
M.toast({html:"캐릭터 정보 공개설정이 필요합니다."});
setTimeout(function() {
window.open("https://maplestory.nexon.com/MyMaple/Account/Character/Visibility");
}, 2000);
break;
case 503:
M.toast({html:"메이플스토리가 점검중입니다."});
break;
default:
M.toast({html:"서버와의 통신이 원활하지 않습니다.<br><br>잠시 후에 시도해주세요."});
break;
}
push('/');
}).finally(function() {
callCount--;
});
}
function showValue(value) {
return parseFloat(value).toFixed(2);
}
function goBack() {
push('/');
}
$:
if(isCharacterLoading && isBuff) {
stats = character.analysis.buff;
} else {
stats = character.analysis.default;
}
init();
</script>
<svelte:head>
</svelte:head>
<Loading count={callCount} />
<section>
{#if isCharacterLoading}
<article class="info-box">
<div class="row">
<div class="col s12 m10 l8 offset-m1 offset-l2">
<div class="row">
<div class="col s12 m4 l3">
<div class="card character-card">
<div class="card-stacked">
<div class="card-content">
<div class="character-img">
<img src={character.info.avatar} class="responsive-img" alt="캐릭터">
</div>
<h6 class="pink-text text-accent-3">
<img src={character.info.server.icon} alt={character.info.server.name}>
{character.info.nickname}
</h6>
<div class="job grey-text text-darken-2">
{character.info.job}
</div>
<div class="level grey-text text-darken-2">
Lv.{character.info.level}
</div>
</div>
</div>
</div>
<div class="back-button-box">
<button class="btn waves-light btn red accent-2" on:click={goBack}>
<i class="material-icons">arrow_back</i>
<span>뒤로가기</span>
</button>
</div>
</div>
<div class="col s12 m8 l9">
<div class="card character-card">
<div class="card-stacked">
<div class="card-content">
<div class="buff-switch">
<div class="switch">
<label>
노버프
<input type="checkbox" bind:checked={isBuff}>
<span class="lever"></span>
버프(자벞,링크,노블,영메)
</label>
</div>
</div>
<table class="table-efficiency">
<thead>
<tr>
<th>스탯</th>
<th>효율</th>
</tr>
</thead>
<tbody>
<tr>
<th rowspan="2">{character.info.majorName} 1%</th>
<td>{character.info.majorName} {showValue(stats.efficiency.major.percent)}</td>
</tr>
<tr>
<td>{character.info.attackPowerName} {showValue(stats.efficiency.major.percent / stats.efficiency.attackPower.pure)}</td>
</tr>
<tr>
<th>{character.info.attackPowerName} 1</th>
<td>{character.info.majorName} {showValue(stats.efficiency.attackPower.pure)}</td>
</tr>
<tr>
<th rowspan="5">{character.info.attackPowerName} 1%</th>
<td>{character.info.majorName} {showValue(stats.efficiency.attackPower.percent)}</td>
</tr>
<tr>
<td>{character.info.majorName} {showValue(stats.efficiency.attackPower.percent / stats.efficiency.major.percent)}%</td>
</tr>
<tr>
<td>{character.info.attackPowerName} {showValue(stats.efficiency.attackPower.percent / stats.efficiency.attackPower.pure)}</td>
</tr>
<tr>
<td>데미지(보공) {showValue(stats.efficiency.attackPower.percent / stats.efficiency.damage)}%</td>
</tr>
<tr>
<td>방무 {showValue(stats.efficiency.attackPower.percent / stats.efficiency.ignoreGuard)}%</td>
</tr>
<tr>
<th rowspan="2">데미지(보공) 1%</th>
<td>{character.info.majorName} {showValue(stats.efficiency.damage)}</td>
</tr>
<tr>
<td>방무 {showValue(stats.efficiency.damage / stats.efficiency.ignoreGuard)}%</td>
</tr>
<tr>
<th rowspan="2">크뎀 1%</th>
<td>{character.info.majorName} {showValue(stats.efficiency.criticalDamage)}</td>
</tr>
<tr>
<td>{character.info.majorName} {showValue(stats.efficiency.criticalDamage / stats.efficiency.major.percent)}%</td>
</tr>
<tr>
<th>방무 1%</th>
<td>{character.info.majorName} {showValue(stats.efficiency.ignoreGuard)}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</article>
{/if}
</section>
<style>
section { width:100%; height:100%; display:flex; flex-direction:column; padding:20px 0; }
.info-box { margin:auto 0; }
.info-box > .row > .col > .row > .col { margin-bottom:.5rem; }
.character-card .card-content { text-align:center; }
.character-card .card-content .job { font-size:0.8em }
.character-card .card-content .level { font-size:0.8em }
.character-card .card-content h6 img { width:14px; height:14px; }
.character-card .card-content h6 { font-weight:bold; }
.table-efficiency td, .table-efficiency th { text-align:left; }
.back-button-box button { width:100%; display:block; height:48px; line-height:48px; }
.back-button-box button i.material-icons { vertical-align:middle; }
.back-button-box button span { vertical-align:middle; }
.buff-switch .switch label input[type=checkbox]:checked+.lever:after { background-color:#e57373; }
.buff-switch .switch label input[type=checkbox]:not(:checked)+.lever { background-color:#ffcdd2; }
.buff-switch .switch label input[type=checkbox]:checked+.lever { background-color:#ef9a9a ; }
</style>
\ No newline at end of file
......@@ -38,6 +38,10 @@
</p>
<p>
<a href="https://maplestory.nexon.com/MyMaple/Account/Character/Visibility" target="_BLANK">공개설정하러 가기</a>
</p>
<p class="red-text text-accent-1">
<i class="material-icons">info_outline</i>
<span>제논, 데몬어벤져는 지원하지 않습니다.</span>
</p>
</div>
</div>
......@@ -47,9 +51,11 @@
</section>
<style>
section { width:100%; height:100%; display:flex; flex-direction:column; justify-content:center; }
.search-box { width:100%; }
section { width:100%; height:100%; display:flex; flex-direction:column; }
.search-box { width:100%; margin:auto 0; }
h4 { font-size:20px; text-align:center; }
.input-field { padding-right:70px; }
.input-field button { position:absolute; right:0; top:7px; }
.card-action i.material-icons { font-size:1.1em; vertical-align:middle; }
.card-action i.material-icons ~ span { vertical-align:middle; }
</style>
\ No newline at end of file
......