오윤석

Merge branch 'master' into release

1 +const analyzeStats = function(characterInfo, analysisEquipment) {
2 + const jobModel = require('./job');
3 + const job = jobModel[characterInfo.character.job];
4 + const jobDefault = jobModel.default;
5 + const weaponConst = require('./weapon')[analysisEquipment.weapon] || 1;
6 +
7 + let rebootDamage = 0;
8 + if (characterInfo.character.server.name.indexOf("리부트") == 0) {
9 + // 리부트, 리부트2 월드 반영
10 + rebootDamage = parseInt(characterInfo.character.level / 2);
11 + }
12 +
13 + const stats = {
14 + major: {
15 + pure: 0,
16 + percent: analysisEquipment.majorPercent +
17 + job.stats.passive.major.percent +
18 + jobDefault.stats.passive.major.percent,
19 + added: 0
20 + },
21 + minor: characterInfo.stats.minor,
22 + damage: {
23 + all: characterInfo.stats.damageHyper +
24 + analysisEquipment.damagePercent +
25 + job.stats.passive.damage.all +
26 + jobDefault.stats.passive.damage.all +
27 + rebootDamage,
28 + boss: characterInfo.stats.bossAttackDamage
29 + },
30 + finalDamage: job.stats.passive.finalDamage,
31 + criticalDamage: characterInfo.stats.criticalDamage + jobDefault.stats.passive.criticalDamage,
32 + attackPower: {
33 + pure: 0,
34 + percent: analysisEquipment.attackPowerPercent +
35 + job.stats.passive.attackPower.percent
36 + },
37 + ignoreGuard: characterInfo.stats.ignoreGuard
38 + };
39 +
40 + stats.major.added = characterInfo.stats.majorHyper +
41 + analysisEquipment.majorArcane +
42 + jobDefault.stats.passive.major.added;
43 + stats.major.pure = (characterInfo.stats.major - stats.major.added) / (1 + stats.major.percent / 100);
44 +
45 + stats.attackPower.pure = characterInfo.stats.statAttackPower * 100 / (characterInfo.stats.major * 4 + stats.minor) / job.jobConst / weaponConst / (1 + stats.attackPower.percent / 100) / (1 + stats.damage.all / 100) / (1 + stats.finalDamage / 100);
46 +
47 + return stats;
48 +}
49 +
50 +const calculateEfficiency = function(stats, job, weapon) {
51 + const efficiency = {
52 + major: {
53 + pure: 1,
54 + percent: 0
55 + },
56 + attackPower: {
57 + pure: 0,
58 + percent: 0,
59 + },
60 + damage: 0,
61 + criticalDamage: 0,
62 + ignoreGuard: 0
63 + };
64 +
65 + const defaultPower = calculatePower(stats, job, weapon);
66 +
67 + stats.major.pure += 1;
68 + const majorPure = calculatePower(stats, job, weapon) - defaultPower;
69 + stats.major.pure -= 1;
70 +
71 + if (majorPure == 0)
72 + return efficiency;
73 +
74 + stats.major.percent += 1;
75 + efficiency.major.percent = (calculatePower(stats, job, weapon) - defaultPower) / majorPure;
76 + stats.major.percent -= 1;
77 +
78 + stats.attackPower.pure += 1;
79 + efficiency.attackPower.pure = (calculatePower(stats, job, weapon) - defaultPower) / majorPure;
80 + stats.attackPower.pure -= 1;
81 +
82 + stats.attackPower.percent += 1;
83 + efficiency.attackPower.percent = (calculatePower(stats, job, weapon) - defaultPower) / majorPure;
84 + stats.attackPower.percent -= 1;
85 +
86 + stats.damage.all += 1;
87 + efficiency.damage = (calculatePower(stats, job, weapon) - defaultPower) / majorPure;
88 + stats.damage.all -= 1;
89 +
90 + stats.criticalDamage += 1;
91 + efficiency.criticalDamage = (calculatePower(stats, job, weapon) - defaultPower) / majorPure;
92 + stats.criticalDamage -= 1;
93 +
94 + // 곱연산
95 + const ignoreGuardSaved = stats.ignoreGuard;
96 + stats.ignoreGuard = (1 - (1 - stats.ignoreGuard / 100) * 0.99) * 100;
97 + efficiency.ignoreGuard = (calculatePower(stats, job, weapon) - defaultPower) / majorPure;
98 + stats.ignoreGuard = ignoreGuardSaved;
99 +
100 + return efficiency;
101 +}
102 +
103 +// 버프 적용 스탯 구하기
104 +const getBuffStats = function(stats, job) {
105 + const jobModel = require('./job');
106 + const buff = jobModel[job].stats.active;
107 + const defaultBuff = jobModel.default.stats.active;
108 +
109 + return {
110 + major: {
111 + pure: stats.major.pure + buff.major.pure,
112 + percent: stats.major.percent + buff.major.percent,
113 + added: stats.major.added
114 + },
115 + minor: stats.minor,
116 + damage: {
117 + all: stats.damage.all + buff.damage.all + defaultBuff.damage.all,
118 + boss: stats.damage.boss + buff.damage.boss + defaultBuff.damage.boss
119 + },
120 + finalDamage: stats.finalDamage,
121 + criticalDamage: stats.criticalDamage + buff.criticalDamage + defaultBuff.criticalDamage,
122 + attackPower: {
123 + pure: stats.attackPower.pure + buff.attackPower.pure,
124 + percent: stats.attackPower.percent + buff.attackPower.percent + defaultBuff.attackPower.percent
125 + },
126 + ignoreGuard: (1 - (1 - (stats.ignoreGuard / 100)) * (1 - (buff.ignoreGuard / 100)) * (1 - (defaultBuff.ignoreGuard / 100))) * 100
127 + };
128 +}
129 +
130 +// 크리티컬 데미지, 보스 공격력, 방어율 무시를 반영하여 방어율 300% 몬스터 공격시 데미지 산출 값
131 +const calculatePower = function(stats, job, weapon) {
132 + const jobConst = require('./job')[job].jobConst;
133 + const weaponConst = require('./weapon')[weapon];
134 + return Math.max(
135 + (
136 + (stats.major.pure * (1 + stats.major.percent / 100) + stats.major.added) * 4 +
137 + stats.minor
138 + ) *
139 + 0.01 *
140 + (stats.attackPower.pure * (1 + stats.attackPower.percent / 100)) *
141 + jobConst *
142 + weaponConst *
143 + (1 + stats.damage.all / 100 + stats.damage.boss / 100) *
144 + (1 + stats.finalDamage / 100) *
145 + (1.35 + stats.criticalDamage / 100) *
146 + (1 - 3 * (1 - stats.ignoreGuard / 100)),
147 + 1);
148 +}
149 +
150 +module.exports = {
151 + analyzeStats: analyzeStats,
152 + calculateEfficiency: calculateEfficiency,
153 + getBuffStats: getBuffStats,
154 + calculatePower: calculatePower,
155 +}
...\ No newline at end of file ...\ No newline at end of file
1 +axios = require('axios');
2 +
3 +const crwalCharacterCode = async function(nickname, isReboot = false) {
4 + try {
5 + const resp = await axios.get("https://maplestory.nexon.com/Ranking/World/Total?c=" + encodeURI(nickname) + "&w=" + (isReboot ? "0" : "254"));
6 +
7 + const regex = new RegExp(`<dt><a href=\\"\\/Common\\/Character\\/Detail\\/[^\\?]+?\\?p=(.+?)\\"\\s+target=.+?\\/>${nickname}<\\/a><\\/dt>`);
8 + const regexResult = regex.exec(resp.data);
9 +
10 + if (!regexResult) {
11 + if (isReboot)
12 + return -2;
13 + else
14 + return await crwalCharacterCode(nickname, true);
15 + }
16 +
17 + return regexResult[1];
18 + } catch (error) {
19 + console.log(error);
20 + return -1;
21 + }
22 +};
23 +
24 +const getCharacterInfo = async function(nickname, characterCode) {
25 + try {
26 + const resp = await axios.get("https://maplestory.nexon.com/Common/Character/Detail/" + encodeURI(nickname) + "?p=" + characterCode);
27 +
28 + if (resp.data.indexOf("공개하지 않은 정보입니다.") >= 0) {
29 + throw new Error("private_character");
30 + }
31 +
32 + if (resp.data.indexOf("메이플스토리 게임 점검 중에는 이용하실 수 없습니다.") >= 0) {
33 + throw new Error("game_checking");
34 + }
35 +
36 + const character = {
37 + nickname: nickname,
38 + characterCode: characterCode,
39 + job: null,
40 + level: null,
41 + avatar: null,
42 + server: {
43 + icon: null,
44 + name: null
45 + },
46 + majorName: null,
47 + attackPowerName: null
48 + };
49 + const stats = {
50 + major: 0,
51 + minor: 0,
52 + majorHyper: 0,
53 + damageHyper: 0,
54 + criticalDamage: 0,
55 + bossAttackDamage: 0,
56 + ignoreGuard: 0,
57 + statAttackPower: 0
58 + };
59 +
60 + const { JSDOM } = require('jsdom');
61 + const dom = new JSDOM(resp.data);
62 + const $ = (require('jquery'))(dom.window);
63 +
64 + const jobModel = require('./job');
65 + const statModel = require('./stat');
66 +
67 + character.job = $(".tab01_con_wrap .table_style01:eq(0) tbody tr:eq(0) td:eq(1) span").text();
68 + character.level = parseInt($(".char_info dl:eq(0) dd").text().substring(3));
69 + character.avatar = $(".char_img img").attr("src");
70 + character.server = {
71 + name: $(".char_info dl:eq(2) dd").text(),
72 + icon: $(".char_info dl:eq(2) dd img").attr("src")
73 + };
74 + character.majorName = jobModel[character.job].major;
75 + character.attackPowerName = character.majorName == "INT" ? "마력" : "공격력";
76 +
77 + const $statInfo = $(".tab01_con_wrap .table_style01:eq(1)");
78 + $("tbody tr", $statInfo).each(function() {
79 + if ($("th", this).length == 1) {
80 + if ($("th span", this).text() == "하이퍼스탯") {
81 + const values = $("td span", this).html().split("<br>");
82 +
83 + const regexMajor = new RegExp(`${statModel[character.majorName].korean} (\\d+) 증가`);
84 + const regexDamage = new RegExp(`^데미지 (\\d+)% 증가`);
85 +
86 + let regexResult;
87 + for (let i = 0; i < values.length; i++) {
88 + if (regexResult = regexMajor.exec(values[i]))
89 + stats['majorHyper'] = parseInt(regexResult[1]);
90 + else if (regexResult = regexDamage.exec(values[i]))
91 + stats['damageHyper'] = parseInt(regexResult[1]);
92 + }
93 + }
94 + } else {
95 + for (let i = 0; i < 2; i++) {
96 + const statName = $(`th:eq(${i}) span`, this).text();
97 + const value = $(`td:eq(${i}) span`, this).text().replace(/\,/g, "");
98 +
99 + switch (statName) {
100 + case character.majorName:
101 + stats['major'] = parseInt(value);
102 + break;
103 + case jobModel[character.job].minor:
104 + stats['minor'] = parseInt(value);
105 + break;
106 + case "크리티컬 데미지":
107 + stats['criticalDamage'] = parseInt(value);
108 + break;
109 + case "보스공격력":
110 + stats['bossAttackDamage'] = parseInt(value);
111 + break;
112 + case "방어율무시":
113 + stats['ignoreGuard'] = parseInt(value);
114 + break;
115 + case "스탯공격력":
116 + stats['statAttackPower'] = parseInt(value.split(' ~ ')[1]);
117 + }
118 + }
119 + }
120 + });
121 +
122 + return {
123 + character: character,
124 + stats: stats
125 + };
126 + } catch (error) {
127 + console.log(error);
128 + if (error.message == "private_character")
129 + return -1;
130 + else if (error.message == "game_checking")
131 + return -2;
132 + else
133 + return -999;
134 + }
135 +}
136 +
137 +const analyzeEquipment = async function(nickname, characterCode, job) {
138 + try {
139 + const resp = await axios.get("https://maplestory.nexon.com/Common/Character/Detail/" + encodeURI(nickname) + "/Equipment?p=" + characterCode);
140 +
141 + if (resp.data.indexOf("공개하지 않은 정보입니다.") >= 0) {
142 + throw new Error("private_character");
143 + }
144 +
145 + if (resp.data.indexOf("메이플스토리 게임 점검 중에는 이용하실 수 없습니다.") >= 0) {
146 + throw new Error("game_checking");
147 + }
148 +
149 + const { JSDOM } = require('jsdom');
150 + const dom = new JSDOM(resp.data);
151 + const $ = (require('jquery'))(dom.window);
152 +
153 + // 아케인심볼 분석
154 + let majorArcane = 0;
155 + const arcaneURLs = [];
156 + $(".tab03_con_wrap .arcane_weapon_wrap .item_pot li span a").each(async function() {
157 + if (!!$(this).attr("href"))
158 + arcaneURLs.push("https://maplestory.nexon.com" + $(this).attr("href"));
159 + });
160 +
161 + for (let i = 0; i < arcaneURLs.length; i++) {
162 + const equipmentResp = await axios.get(arcaneURLs[i], {
163 + headers: {
164 + 'X-Requested-With': 'XMLHttpRequest'
165 + }
166 + });
167 +
168 + const equipmentDom = new JSDOM(equipmentResp.data.view);
169 + const $equipment = (require('jquery'))(equipmentDom.window);
170 +
171 + majorArcane += parseInt($equipment(".stet_info ul li:eq(2) .point_td font:eq(0)").text().substring(1));
172 + }
173 +
174 + // 장비 분석
175 + const jobModel = require('./job');
176 +
177 + let damagePercent = 0;
178 + let majorPercent = 0;
179 + let attackPowerPercent = 0;
180 + let weapon = undefined;
181 + const equipmentURLs = [];
182 + $(".tab01_con_wrap .weapon_wrap .item_pot li span a").each(async function() {
183 + equipmentURLs.push("https://maplestory.nexon.com" + $(this).attr("href"));
184 + });
185 +
186 + for (let i = 0; i < equipmentURLs.length; i++) {
187 + const equipmentResp = await axios.get(equipmentURLs[i], {
188 + headers: {
189 + 'X-Requested-With': 'XMLHttpRequest'
190 + }
191 + });
192 +
193 + const equipmentDom = new JSDOM(equipmentResp.data.view);
194 + const $equipment = (require('jquery'))(equipmentDom.window);
195 +
196 + const equipmentType = $equipment(".item_ability .ablilty02:eq(1) .job_name em").text();
197 + if (equipmentType.indexOf("손무기") >= 0 && equipmentType.indexOf("블레이드") < 0 && equipmentType.indexOf("대검") < 0) {
198 + weapon = equipmentType.split(" (")[0];
199 + }
200 +
201 + $equipment(".stet_info ul li").each(function() {
202 + const regexMajor1 = new RegExp(`${jobModel[job].major} : \\+(\\d+)%`);
203 + const regexMajor2 = new RegExp(`올스탯 : \\+(\\d+)%`);
204 + const regexAttackPower = (jobModel[job].major == "INT") ?
205 + new RegExp(`마력 : \\+(\\d+)%`) :
206 + new RegExp(`공격력 : \\+(\\d+)%`);
207 + const regexDamage = new RegExp(`^데미지 : \\+(\\d+)%`);
208 +
209 + if ($(this).find(".stet_th span").text() == "올스탯") {
210 + majorPercent += parseInt($(this).find(".point_td font:eq(0)").text().substring(1));
211 + } else if ($(this).find(".stet_th span").text().indexOf("잠재옵션") >= 0) {
212 + const values = $(this).find(".point_td").html().split("<br>");
213 + for (let j = 0; j < values.length; j++) {
214 + const value = values[j].trim();
215 + let regexResult;
216 +
217 + if (regexResult = (regexMajor1.exec(value) || regexMajor2.exec(value))) {
218 + majorPercent += parseInt(regexResult[1]);
219 + } else if (regexResult = regexAttackPower.exec(value)) {
220 + attackPowerPercent += parseInt(regexResult[1]);
221 + } else if (regexResult = regexDamage.exec(value)) {
222 + damagePercent += parseInt(regexResult[1]);
223 + }
224 + }
225 + }
226 + })
227 + }
228 +
229 + return {
230 + majorArcane: majorArcane,
231 + majorPercent: majorPercent,
232 + attackPowerPercent: attackPowerPercent,
233 + damagePercent: damagePercent,
234 + weapon: weapon
235 + };
236 + } catch (error) {
237 + console.log(error);
238 + if (error.message == "private_character")
239 + return -1;
240 + else if (error.message == "game_checking")
241 + return -2;
242 + else
243 + return -999;
244 + }
245 +}
246 +
247 +module.exports = {
248 + crwalCharacterCode: crwalCharacterCode,
249 + getCharacterInfo: getCharacterInfo,
250 + analyzeEquipment: analyzeEquipment,
251 +}
...\ No newline at end of file ...\ No newline at end of file
1 -axios = require('axios'); 1 +const characterModel = require('../model/character');
2 - 2 +const analysisModel = require('../model/analysis');
3 -const crwalCharacterCode = async function(nickname, isReboot = false) {
4 - try {
5 - const resp = await axios.get("https://maplestory.nexon.com/Ranking/World/Total?c=" + encodeURI(nickname) + "&w=" + (isReboot ? "0" : "254"));
6 -
7 - const regex = new RegExp(`<dt><a href=\\"\\/Common\\/Character\\/Detail\\/[^\\?]+?\\?p=(.+?)\\"\\s+target=.+?\\/>${nickname}<\\/a><\\/dt>`);
8 - const regexResult = regex.exec(resp.data);
9 -
10 - if (!regexResult) {
11 - if (isReboot)
12 - return -2;
13 - else
14 - return await crwalCharacterCode(nickname, true);
15 - }
16 -
17 - return regexResult[1];
18 - } catch (error) {
19 - console.log(error);
20 - return -1;
21 - }
22 -}
23 -
24 -const getCharacterInfo = async function(nickname, characterCode) {
25 - try {
26 - const resp = await axios.get("https://maplestory.nexon.com/Common/Character/Detail/" + encodeURI(nickname) + "?p=" + characterCode);
27 -
28 - if (resp.data.indexOf("공개하지 않은 정보입니다.") >= 0) {
29 - throw new Error("private_character");
30 - }
31 -
32 - if (resp.data.indexOf("메이플스토리 게임 점검 중에는 이용하실 수 없습니다.") >= 0) {
33 - throw new Error("game_checking");
34 - }
35 -
36 - const character = {
37 - nickname: nickname,
38 - characterCode: characterCode,
39 - job: null,
40 - level: null,
41 - avatar: null,
42 - server: {
43 - icon: null,
44 - name: null
45 - },
46 - majorName: null,
47 - attackPowerName: null
48 - };
49 - const stats = {
50 - major: 0,
51 - minor: 0,
52 - majorHyper: 0,
53 - damageHyper: 0,
54 - criticalDamage: 0,
55 - bossAttackDamage: 0,
56 - ignoreGuard: 0,
57 - statAttackPower: 0
58 - };
59 -
60 - const { JSDOM } = require('jsdom');
61 - const dom = new JSDOM(resp.data);
62 - const $ = (require('jquery'))(dom.window);
63 -
64 - const jobModel = require('../model/job');
65 - const statModel = require('../model/stat');
66 -
67 - character.job = $(".tab01_con_wrap .table_style01:eq(0) tbody tr:eq(0) td:eq(1) span").text();
68 - character.level = parseInt($(".char_info dl:eq(0) dd").text().substring(3));
69 - character.avatar = $(".char_img img").attr("src");
70 - character.server = {
71 - name: $(".char_info dl:eq(2) dd").text(),
72 - icon: $(".char_info dl:eq(2) dd img").attr("src")
73 - };
74 - character.majorName = jobModel[character.job].major;
75 - character.attackPowerName = character.majorName == "INT" ? "마력" : "공격력";
76 -
77 - const $statInfo = $(".tab01_con_wrap .table_style01:eq(1)");
78 - $("tbody tr", $statInfo).each(function() {
79 - if ($("th", this).length == 1) {
80 - if ($("th span", this).text() == "하이퍼스탯") {
81 - const values = $("td span", this).html().split("<br>");
82 -
83 - const regexMajor = new RegExp(`${statModel[character.majorName].korean} (\\d+) 증가`);
84 - const regexDamage = new RegExp(`^데미지 (\\d+)% 증가`);
85 -
86 - let regexResult;
87 - for (let i = 0; i < values.length; i++) {
88 - if (regexResult = regexMajor.exec(values[i]))
89 - stats['majorHyper'] = parseInt(regexResult[1]);
90 - else if (regexResult = regexDamage.exec(values[i]))
91 - stats['damageHyper'] = parseInt(regexResult[1]);
92 - }
93 - }
94 - } else {
95 - for (let i = 0; i < 2; i++) {
96 - const statName = $(`th:eq(${i}) span`, this).text();
97 - const value = $(`td:eq(${i}) span`, this).text().replace(/\,/g, "");
98 -
99 - switch (statName) {
100 - case character.majorName:
101 - stats['major'] = parseInt(value);
102 - break;
103 - case jobModel[character.job].minor:
104 - stats['minor'] = parseInt(value);
105 - break;
106 - case "크리티컬 데미지":
107 - stats['criticalDamage'] = parseInt(value);
108 - break;
109 - case "보스공격력":
110 - stats['bossAttackDamage'] = parseInt(value);
111 - break;
112 - case "방어율무시":
113 - stats['ignoreGuard'] = parseInt(value);
114 - break;
115 - case "스탯공격력":
116 - stats['statAttackPower'] = parseInt(value.split(' ~ ')[1]);
117 - }
118 - }
119 - }
120 - });
121 -
122 - return {
123 - character: character,
124 - stats: stats
125 - };
126 - } catch (error) {
127 - console.log(error);
128 - if (error.message == "private_character")
129 - return -1;
130 - else if (error.message == "game_checking")
131 - return -2;
132 - else
133 - return -999;
134 - }
135 -}
136 -
137 -const analyzeEquipment = async function(nickname, characterCode, job) {
138 - try {
139 - const resp = await axios.get("https://maplestory.nexon.com/Common/Character/Detail/" + encodeURI(nickname) + "/Equipment?p=" + characterCode);
140 -
141 - if (resp.data.indexOf("공개하지 않은 정보입니다.") >= 0) {
142 - throw new Error("private_character");
143 - }
144 -
145 - if (resp.data.indexOf("메이플스토리 게임 점검 중에는 이용하실 수 없습니다.") >= 0) {
146 - throw new Error("game_checking");
147 - }
148 -
149 - const { JSDOM } = require('jsdom');
150 - const dom = new JSDOM(resp.data);
151 - const $ = (require('jquery'))(dom.window);
152 -
153 - // 아케인심볼 분석
154 - let majorArcane = 0;
155 - const arcaneURLs = [];
156 - $(".tab03_con_wrap .arcane_weapon_wrap .item_pot li span a").each(async function() {
157 - if (!!$(this).attr("href"))
158 - arcaneURLs.push("https://maplestory.nexon.com" + $(this).attr("href"));
159 - });
160 -
161 - for (let i = 0; i < arcaneURLs.length; i++) {
162 - const equipmentResp = await axios.get(arcaneURLs[i], {
163 - headers: {
164 - 'X-Requested-With': 'XMLHttpRequest'
165 - }
166 - });
167 -
168 - const equipmentDom = new JSDOM(equipmentResp.data.view);
169 - const $equipment = (require('jquery'))(equipmentDom.window);
170 -
171 - majorArcane += parseInt($equipment(".stet_info ul li:eq(2) .point_td font:eq(0)").text().substring(1));
172 - }
173 -
174 - // 장비 분석
175 - const jobModel = require('../model/job');
176 -
177 - let damagePercent = 0;
178 - let majorPercent = 0;
179 - let attackPowerPercent = 0;
180 - let weapon = undefined;
181 - const equipmentURLs = [];
182 - $(".tab01_con_wrap .weapon_wrap .item_pot li span a").each(async function() {
183 - equipmentURLs.push("https://maplestory.nexon.com" + $(this).attr("href"));
184 - });
185 -
186 - for (let i = 0; i < equipmentURLs.length; i++) {
187 - const equipmentResp = await axios.get(equipmentURLs[i], {
188 - headers: {
189 - 'X-Requested-With': 'XMLHttpRequest'
190 - }
191 - });
192 -
193 - const equipmentDom = new JSDOM(equipmentResp.data.view);
194 - const $equipment = (require('jquery'))(equipmentDom.window);
195 -
196 - const equipmentType = $equipment(".item_ability .ablilty02:eq(1) .job_name em").text();
197 - if (equipmentType.indexOf("손무기") >= 0 && equipmentType.indexOf("블레이드") < 0 && equipmentType.indexOf("대검") < 0) {
198 - weapon = equipmentType.split(" (")[0];
199 - }
200 -
201 - $equipment(".stet_info ul li").each(function() {
202 - const regexMajor1 = new RegExp(`${jobModel[job].major} : \\+(\\d+)%`);
203 - const regexMajor2 = new RegExp(`올스탯 : \\+(\\d+)%`);
204 - const regexAttackPower = (jobModel[job].major == "INT") ?
205 - new RegExp(`마력 : \\+(\\d+)%`) :
206 - new RegExp(`공격력 : \\+(\\d+)%`);
207 - const regexDamage = new RegExp(`^데미지 : \\+(\\d+)%`);
208 -
209 - if ($(this).find(".stet_th span").text() == "올스탯") {
210 - majorPercent += parseInt($(this).find(".point_td font:eq(0)").text().substring(1));
211 - } else if ($(this).find(".stet_th span").text().indexOf("잠재옵션") >= 0) {
212 - const values = $(this).find(".point_td").html().split("<br>");
213 - for (let j = 0; j < values.length; j++) {
214 - const value = values[j].trim();
215 - let regexResult;
216 -
217 - if (regexResult = (regexMajor1.exec(value) || regexMajor2.exec(value))) {
218 - majorPercent += parseInt(regexResult[1]);
219 - } else if (regexResult = regexAttackPower.exec(value)) {
220 - attackPowerPercent += parseInt(regexResult[1]);
221 - } else if (regexResult = regexDamage.exec(value)) {
222 - damagePercent += parseInt(regexResult[1]);
223 - }
224 - }
225 - }
226 - })
227 - }
228 -
229 - return {
230 - majorArcane: majorArcane,
231 - majorPercent: majorPercent,
232 - attackPowerPercent: attackPowerPercent,
233 - damagePercent: damagePercent,
234 - weapon: weapon
235 - };
236 - } catch (error) {
237 - console.log(error);
238 - if (error.message == "private_character")
239 - return -1;
240 - else if (error.message == "game_checking")
241 - return -2;
242 - else
243 - return -999;
244 - }
245 -}
246 -
247 -const analyzeStats = function(characterInfo, analysisEquipment) {
248 - const jobModel = require('../model/job');
249 - const job = jobModel[characterInfo.character.job];
250 - const jobDefault = jobModel.default;
251 - const weaponConst = require('../model/weapon')[analysisEquipment.weapon] || 1;
252 -
253 - let rebootDamage = 0;
254 - if (characterInfo.character.server.name.indexOf("리부트") == 0) {
255 - // 리부트, 리부트2 월드 반영
256 - rebootDamage = parseInt(characterInfo.character.level / 2);
257 - }
258 -
259 - const stats = {
260 - major: {
261 - pure: 0,
262 - percent: analysisEquipment.majorPercent +
263 - job.stats.passive.major.percent +
264 - jobDefault.stats.passive.major.percent,
265 - added: 0
266 - },
267 - minor: characterInfo.stats.minor,
268 - damage: {
269 - all: characterInfo.stats.damageHyper +
270 - analysisEquipment.damagePercent +
271 - job.stats.passive.damage.all +
272 - jobDefault.stats.passive.damage.all +
273 - rebootDamage,
274 - boss: characterInfo.stats.bossAttackDamage
275 - },
276 - finalDamage: job.stats.passive.finalDamage,
277 - criticalDamage: characterInfo.stats.criticalDamage + jobDefault.stats.passive.criticalDamage,
278 - attackPower: {
279 - pure: 0,
280 - percent: analysisEquipment.attackPowerPercent +
281 - job.stats.passive.attackPower.percent
282 - },
283 - ignoreGuard: characterInfo.stats.ignoreGuard
284 - };
285 -
286 - stats.major.added = characterInfo.stats.majorHyper +
287 - analysisEquipment.majorArcane +
288 - jobDefault.stats.passive.major.added;
289 - stats.major.pure = (characterInfo.stats.major - stats.major.added) / (1 + stats.major.percent / 100);
290 -
291 - stats.attackPower.pure = characterInfo.stats.statAttackPower * 100 / (characterInfo.stats.major * 4 + stats.minor) / job.jobConst / weaponConst / (1 + stats.attackPower.percent / 100) / (1 + stats.damage.all / 100) / (1 + stats.finalDamage / 100);
292 -
293 - return stats;
294 -}
295 -
296 -const calculateEfficiency = function(stats, job, weapon) {
297 - const efficiency = {
298 - major: {
299 - pure: 1,
300 - percent: 0
301 - },
302 - attackPower: {
303 - pure: 0,
304 - percent: 0,
305 - },
306 - damage: 0,
307 - criticalDamage: 0,
308 - ignoreGuard: 0
309 - };
310 -
311 - const defaultPower = calculatePower(stats, job, weapon);
312 -
313 - stats.major.pure += 1;
314 - const majorPure = calculatePower(stats, job, weapon) - defaultPower;
315 - stats.major.pure -= 1;
316 -
317 - if (majorPure == 0)
318 - return efficiency;
319 -
320 - stats.major.percent += 1;
321 - efficiency.major.percent = (calculatePower(stats, job, weapon) - defaultPower) / majorPure;
322 - stats.major.percent -= 1;
323 -
324 - stats.attackPower.pure += 1;
325 - efficiency.attackPower.pure = (calculatePower(stats, job, weapon) - defaultPower) / majorPure;
326 - stats.attackPower.pure -= 1;
327 -
328 - stats.attackPower.percent += 1;
329 - efficiency.attackPower.percent = (calculatePower(stats, job, weapon) - defaultPower) / majorPure;
330 - stats.attackPower.percent -= 1;
331 -
332 - stats.damage.all += 1;
333 - efficiency.damage = (calculatePower(stats, job, weapon) - defaultPower) / majorPure;
334 - stats.damage.all -= 1;
335 -
336 - stats.criticalDamage += 1;
337 - efficiency.criticalDamage = (calculatePower(stats, job, weapon) - defaultPower) / majorPure;
338 - stats.criticalDamage -= 1;
339 -
340 - // 곱연산
341 - const ignoreGuardSaved = stats.ignoreGuard;
342 - stats.ignoreGuard = (1 - (1 - stats.ignoreGuard / 100) * 0.99) * 100;
343 - efficiency.ignoreGuard = (calculatePower(stats, job, weapon) - defaultPower) / majorPure;
344 - stats.ignoreGuard = ignoreGuardSaved;
345 -
346 - return efficiency;
347 -}
348 -
349 -// 버프 적용 스탯 구하기
350 -const getBuffStats = function(stats, job) {
351 - const jobModel = require('../model/job');
352 - const buff = jobModel[job].stats.active;
353 - const defaultBuff = jobModel.default.stats.active;
354 -
355 - return {
356 - major: {
357 - pure: stats.major.pure + buff.major.pure,
358 - percent: stats.major.percent + buff.major.percent,
359 - added: stats.major.added
360 - },
361 - minor: stats.minor,
362 - damage: {
363 - all: stats.damage.all + buff.damage.all + defaultBuff.damage.all,
364 - boss: stats.damage.boss + buff.damage.boss + defaultBuff.damage.boss
365 - },
366 - finalDamage: stats.finalDamage,
367 - criticalDamage: stats.criticalDamage + buff.criticalDamage + defaultBuff.criticalDamage,
368 - attackPower: {
369 - pure: stats.attackPower.pure + buff.attackPower.pure,
370 - percent: stats.attackPower.percent + buff.attackPower.percent + defaultBuff.attackPower.percent
371 - },
372 - ignoreGuard: (1 - (1 - (stats.ignoreGuard / 100)) * (1 - (buff.ignoreGuard / 100)) * (1 - (defaultBuff.ignoreGuard / 100))) * 100
373 - };
374 -}
375 -
376 -// 크리티컬 데미지, 보스 공격력, 방어율 무시를 반영하여 방어율 300% 몬스터 공격시 데미지 산출 값
377 -const calculatePower = function(stats, job, weapon) {
378 - const jobConst = require('../model/job')[job].jobConst;
379 - const weaponConst = require('../model/weapon')[weapon];
380 - return Math.max(
381 - (
382 - (stats.major.pure * (1 + stats.major.percent / 100) + stats.major.added) * 4 +
383 - stats.minor
384 - ) *
385 - 0.01 *
386 - (stats.attackPower.pure * (1 + stats.attackPower.percent / 100)) *
387 - jobConst *
388 - weaponConst *
389 - (1 + stats.damage.all / 100 + stats.damage.boss / 100) *
390 - (1 + stats.finalDamage / 100) *
391 - (1.35 + stats.criticalDamage / 100) *
392 - (1 - 3 * (1 - stats.ignoreGuard / 100)),
393 - 1);
394 -}
395 3
396 module.exports = { 4 module.exports = {
397 getCharacter: async function(req, res) { 5 getCharacter: async function(req, res) {
...@@ -401,7 +9,7 @@ module.exports = { ...@@ -401,7 +9,7 @@ module.exports = {
401 } 9 }
402 10
403 const nickname = req.query.nickname; 11 const nickname = req.query.nickname;
404 - const characterCode = await crwalCharacterCode(req.query.nickname); 12 + const characterCode = await characterModel.crwalCharacterCode(req.query.nickname);
405 13
406 if (characterCode == -1) { 14 if (characterCode == -1) {
407 res.status(500).send(); 15 res.status(500).send();
...@@ -411,7 +19,7 @@ module.exports = { ...@@ -411,7 +19,7 @@ module.exports = {
411 return; 19 return;
412 } 20 }
413 21
414 - const characterInfo = await getCharacterInfo(nickname, characterCode); 22 + const characterInfo = await characterModel.getCharacterInfo(nickname, characterCode);
415 if (characterInfo == -1) { 23 if (characterInfo == -1) {
416 // 접근 권한 설정 필요 24 // 접근 권한 설정 필요
417 res.status(403).send(); 25 res.status(403).send();
...@@ -425,7 +33,7 @@ module.exports = { ...@@ -425,7 +33,7 @@ module.exports = {
425 return; 33 return;
426 } 34 }
427 35
428 - const analysisEquipment = await analyzeEquipment(nickname, characterCode, characterInfo.character.job); 36 + const analysisEquipment = await characterModel.analyzeEquipment(nickname, characterCode, characterInfo.character.job);
429 if (analysisEquipment == -1) { 37 if (analysisEquipment == -1) {
430 // 접근 권한 설정 필요 38 // 접근 권한 설정 필요
431 res.status(403).send(); 39 res.status(403).send();
...@@ -439,21 +47,19 @@ module.exports = { ...@@ -439,21 +47,19 @@ module.exports = {
439 return; 47 return;
440 } 48 }
441 49
442 - const stats = analyzeStats(characterInfo, analysisEquipment); 50 + const stats = analysisModel.analyzeStats(characterInfo, analysisEquipment);
443 - const buffStats = getBuffStats(stats, characterInfo.character.job); 51 + const buffStats = analysisModel.getBuffStats(stats, characterInfo.character.job);
444 - const efficiency = calculateEfficiency(stats, characterInfo.character.job, analysisEquipment.weapon);
445 - const buffEfficiency = calculateEfficiency(buffStats, characterInfo.character.job, analysisEquipment.weapon);
446 52
447 const result = { 53 const result = {
448 info: characterInfo.character, 54 info: characterInfo.character,
449 analysis: { 55 analysis: {
450 default: { 56 default: {
451 stats: stats, 57 stats: stats,
452 - efficiency: efficiency 58 + efficiency: analysisModel.calculateEfficiency(stats, characterInfo.character.job, analysisEquipment.weapon)
453 }, 59 },
454 buff: { 60 buff: {
455 stats: buffStats, 61 stats: buffStats,
456 - efficiency: buffEfficiency 62 + efficiency: analysisModel.calculateEfficiency(buffStats, characterInfo.character.job, analysisEquipment.weapon)
457 } 63 }
458 } 64 }
459 }; 65 };
......