Showing
2 changed files
with
201 additions
and
8 deletions
app/node/model/weapon.js
0 → 100644
1 | +module.exports = { | ||
2 | + '한손검': 1.2, | ||
3 | + '한손도끼': 1.2, | ||
4 | + '한손둔기': 1.2, | ||
5 | + '스태프': 1, | ||
6 | + '완드': 1, | ||
7 | + '샤이닝 로드': 1.2, | ||
8 | + '단검': 1.3, | ||
9 | + '케인': 1.3, | ||
10 | + '데스페라도': 1.3, | ||
11 | + '에너지소드': 1.5, | ||
12 | + '소울 슈터': 1.7, | ||
13 | + 'ESP 리미터': 1.2, | ||
14 | + '체인': 1.3, | ||
15 | + '매직 건틀렛': 1.2, | ||
16 | + '부채': 1.3, | ||
17 | + '튜너': 1.3, | ||
18 | + '두손검': 1.34, | ||
19 | + '두손도끼': 1.34, | ||
20 | + '두손둔기': 1.34, | ||
21 | + '창': 1.34, | ||
22 | + '폴암': 1.49, | ||
23 | + '태도': 1.34, | ||
24 | + '건틀렛 리볼버': 1.7, | ||
25 | + '활': 1.3, | ||
26 | + '석궁': 1.35, | ||
27 | + '듀얼 보우건': 1.3, | ||
28 | + '에인션트 보우': 1.3, | ||
29 | + '아대': 1.75, | ||
30 | + '건': 1.5, | ||
31 | + '너클': 1.7, | ||
32 | + '핸드캐논': 1.5 | ||
33 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
... | @@ -144,6 +144,7 @@ const analyzeEquipment = async function(nickname, characterCode, job) { | ... | @@ -144,6 +144,7 @@ const analyzeEquipment = async function(nickname, characterCode, job) { |
144 | let damagePercent = 0; | 144 | let damagePercent = 0; |
145 | let majorPercent = 0; | 145 | let majorPercent = 0; |
146 | let attackPowerPercent = 0; | 146 | let attackPowerPercent = 0; |
147 | + let weapon = undefined; | ||
147 | const equipmentURLs = []; | 148 | const equipmentURLs = []; |
148 | $(".tab01_con_wrap .weapon_wrap .item_pot li span a").each(async function() { | 149 | $(".tab01_con_wrap .weapon_wrap .item_pot li span a").each(async function() { |
149 | equipmentURLs.push("https://maplestory.nexon.com" + $(this).attr("href")); | 150 | equipmentURLs.push("https://maplestory.nexon.com" + $(this).attr("href")); |
... | @@ -159,13 +160,18 @@ const analyzeEquipment = async function(nickname, characterCode, job) { | ... | @@ -159,13 +160,18 @@ const analyzeEquipment = async function(nickname, characterCode, job) { |
159 | const equipmentDom = new JSDOM(equipmentResp.data.view); | 160 | const equipmentDom = new JSDOM(equipmentResp.data.view); |
160 | const $equipment = (require('jquery'))(equipmentDom.window); | 161 | const $equipment = (require('jquery'))(equipmentDom.window); |
161 | 162 | ||
163 | + const equipmentType = $equipment(".item_ability .ablilty02:eq(1) .job_name em").text(); | ||
164 | + if (equipmentType.indexOf("손무기") >= 0 && equipmentType.indexOf("블레이드") < 0 && equipmentType.indexOf("대검") < 0) { | ||
165 | + weapon = equipmentType.split(" (")[0]; | ||
166 | + } | ||
167 | + | ||
162 | $equipment(".stet_info ul li").each(function() { | 168 | $equipment(".stet_info ul li").each(function() { |
163 | - const regexMajor1 = new RegExp(`${jobModel[job].major} : \\+(\\d)%`); | 169 | + const regexMajor1 = new RegExp(`${jobModel[job].major} : \\+(\\d+)%`); |
164 | - const regexMajor2 = new RegExp(`올스탯 : \\+(\\d)%`); | 170 | + const regexMajor2 = new RegExp(`올스탯 : \\+(\\d+)%`); |
165 | const regexAttackPower = (jobModel[job].major == "INT") ? | 171 | const regexAttackPower = (jobModel[job].major == "INT") ? |
166 | - new RegExp(`마력 : \\+(\\d)%`) : | 172 | + new RegExp(`마력 : \\+(\\d+)%`) : |
167 | - new RegExp(`공격력 : \\+(\\d)%`); | 173 | + new RegExp(`공격력 : \\+(\\d+)%`); |
168 | - const regexDamage = new RegExp(`데미지 : \\+(\\d)%`); | 174 | + const regexDamage = new RegExp(`^데미지 : \\+(\\d+)%`); |
169 | 175 | ||
170 | if ($(this).find(".stet_th span").text() == "올스탯") { | 176 | if ($(this).find(".stet_th span").text() == "올스탯") { |
171 | majorPercent += parseInt($(this).find(".point_td font:eq(0)").text().substring(1)); | 177 | majorPercent += parseInt($(this).find(".point_td font:eq(0)").text().substring(1)); |
... | @@ -191,7 +197,8 @@ const analyzeEquipment = async function(nickname, characterCode, job) { | ... | @@ -191,7 +197,8 @@ const analyzeEquipment = async function(nickname, characterCode, job) { |
191 | majorArcane: majorArcane, | 197 | majorArcane: majorArcane, |
192 | majorPercent: majorPercent, | 198 | majorPercent: majorPercent, |
193 | attackPowerPercent: attackPowerPercent, | 199 | attackPowerPercent: attackPowerPercent, |
194 | - damagePercent: damagePercent | 200 | + damagePercent: damagePercent, |
201 | + weapon: weapon | ||
195 | }; | 202 | }; |
196 | } catch (error) { | 203 | } catch (error) { |
197 | console.log(error); | 204 | console.log(error); |
... | @@ -199,6 +206,146 @@ const analyzeEquipment = async function(nickname, characterCode, job) { | ... | @@ -199,6 +206,146 @@ const analyzeEquipment = async function(nickname, characterCode, job) { |
199 | } | 206 | } |
200 | } | 207 | } |
201 | 208 | ||
209 | +const analyzeStats = function(characterInfo, analysisEquipment) { | ||
210 | + const jobModel = require('../model/job'); | ||
211 | + const job = jobModel[characterInfo.character.job]; | ||
212 | + const jobDefault = jobModel.default; | ||
213 | + const weaponConst = require('../model/weapon')[analysisEquipment.weapon] || 1; | ||
214 | + const stats = { | ||
215 | + major: { | ||
216 | + pure: 0, | ||
217 | + percent: analysisEquipment.majorPercent + | ||
218 | + job.stats.passive.major.percent + | ||
219 | + jobDefault.stats.passive.major.percent, | ||
220 | + added: 0 | ||
221 | + }, | ||
222 | + minor: characterInfo.stats.minor, | ||
223 | + damage: { | ||
224 | + all: characterInfo.stats.damageHyper + | ||
225 | + analysisEquipment.damagePercent + | ||
226 | + job.stats.passive.damage.all + | ||
227 | + jobDefault.stats.passive.damage.all, | ||
228 | + boss: characterInfo.stats.bossAttackDamage | ||
229 | + }, | ||
230 | + finalDamage: job.stats.passive.finalDamage, | ||
231 | + criticalDamage: characterInfo.stats.criticalDamage, | ||
232 | + attackPower: { | ||
233 | + pure: 0, | ||
234 | + percent: analysisEquipment.attackPowerPercent + | ||
235 | + job.stats.passive.attackPower.percent | ||
236 | + }, | ||
237 | + ignoreGuard: characterInfo.stats.ignoreGuard | ||
238 | + }; | ||
239 | + | ||
240 | + stats.major.added = characterInfo.stats.majorHyper + | ||
241 | + analysisEquipment.majorArcane; | ||
242 | + stats.major.pure = (characterInfo.stats.major - stats.major.added) / (1 + stats.major.percent / 100); | ||
243 | + | ||
244 | + 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); | ||
245 | + | ||
246 | + return stats; | ||
247 | +} | ||
248 | + | ||
249 | +const calculateEfficiency = function(stats, job, weapon) { | ||
250 | + const efficiency = { | ||
251 | + major: { | ||
252 | + pure: 1, | ||
253 | + percent: 0 | ||
254 | + }, | ||
255 | + attackPower: { | ||
256 | + pure: 0, | ||
257 | + percent: 0, | ||
258 | + }, | ||
259 | + damage: 0, | ||
260 | + criticalDamage: 0, | ||
261 | + ignoreGuard: 0 | ||
262 | + }; | ||
263 | + | ||
264 | + const defaultPower = calculatePower(stats, job, weapon); | ||
265 | + | ||
266 | + stats.major.pure += 1; | ||
267 | + const majorPure = calculatePower(stats, job, weapon) - defaultPower; | ||
268 | + stats.major.pure -= 1; | ||
269 | + | ||
270 | + if (majorPure == 0) | ||
271 | + return efficiency; | ||
272 | + | ||
273 | + stats.major.percent += 1; | ||
274 | + efficiency.major.percent = (calculatePower(stats, job, weapon) - defaultPower) / majorPure; | ||
275 | + stats.major.percent -= 1; | ||
276 | + | ||
277 | + stats.attackPower.pure += 1; | ||
278 | + efficiency.attackPower.pure = (calculatePower(stats, job, weapon) - defaultPower) / majorPure; | ||
279 | + stats.attackPower.pure -= 1; | ||
280 | + | ||
281 | + stats.attackPower.percent += 1; | ||
282 | + efficiency.attackPower.percent = (calculatePower(stats, job, weapon) - defaultPower) / majorPure; | ||
283 | + stats.attackPower.percent -= 1; | ||
284 | + | ||
285 | + stats.damage.all += 1; | ||
286 | + efficiency.damage = (calculatePower(stats, job, weapon) - defaultPower) / majorPure; | ||
287 | + stats.damage.all -= 1; | ||
288 | + | ||
289 | + stats.criticalDamage += 1; | ||
290 | + efficiency.criticalDamage = (calculatePower(stats, job, weapon) - defaultPower) / majorPure; | ||
291 | + stats.criticalDamage -= 1; | ||
292 | + | ||
293 | + // 곱연산 | ||
294 | + const ignoreGuardSaved = stats.ignoreGuard; | ||
295 | + stats.ignoreGuard = (1 - (1 - stats.ignoreGuard / 100) * 0.99) * 100; | ||
296 | + efficiency.ignoreGuard = (calculatePower(stats, job, weapon) - defaultPower) / majorPure; | ||
297 | + stats.ignoreGuard = ignoreGuardSaved; | ||
298 | + | ||
299 | + return efficiency; | ||
300 | +} | ||
301 | + | ||
302 | +// 버프 적용 스탯 구하기 | ||
303 | +const getBuffStats = function(stats, job) { | ||
304 | + const jobModel = require('../model/job'); | ||
305 | + const buff = jobModel[job].stats.active; | ||
306 | + const defaultBuff = jobModel.default.stats.active; | ||
307 | + | ||
308 | + return { | ||
309 | + major: { | ||
310 | + pure: stats.major.pure + buff.major.pure, | ||
311 | + percent: stats.major.percent + buff.major.percent, | ||
312 | + added: stats.major.added | ||
313 | + }, | ||
314 | + minor: stats.minor, | ||
315 | + damage: { | ||
316 | + all: stats.damage.all + buff.damage.all + defaultBuff.damage.all, | ||
317 | + boss: stats.damage.boss + buff.damage.boss + defaultBuff.damage.boss | ||
318 | + }, | ||
319 | + finalDamage: stats.finalDamage, | ||
320 | + criticalDamage: stats.criticalDamage + buff.criticalDamage + defaultBuff.criticalDamage, | ||
321 | + attackPower: { | ||
322 | + pure: stats.attackPower.pure + buff.attackPower.pure, | ||
323 | + percent: stats.attackPower.percent + buff.attackPower.percent + defaultBuff.attackPower.percent | ||
324 | + }, | ||
325 | + ignoreGuard: (1 - (1 - (stats.ignoreGuard / 100)) * (1 - (buff.ignoreGuard / 100)) * (1 - (defaultBuff.ignoreGuard / 100))) * 100 | ||
326 | + }; | ||
327 | +} | ||
328 | + | ||
329 | +// 크리티컬 데미지, 보스 공격력, 방어율 무시를 반영하여 방어율 300% 몬스터 공격시 데미지 산출 값 | ||
330 | +const calculatePower = function(stats, job, weapon) { | ||
331 | + const jobConst = require('../model/job')[job].jobConst; | ||
332 | + const weaponConst = require('../model/weapon')[weapon]; | ||
333 | + return Math.max( | ||
334 | + ( | ||
335 | + (stats.major.pure * (1 + stats.major.percent / 100) + stats.major.added) * 4 + | ||
336 | + stats.minor | ||
337 | + ) * | ||
338 | + 0.01 * | ||
339 | + (stats.attackPower.pure * (1 + stats.attackPower.percent / 100)) * | ||
340 | + jobConst * | ||
341 | + weaponConst * | ||
342 | + (1 + stats.damage.all / 100 + stats.damage.boss / 100) * | ||
343 | + (1 + stats.finalDamage / 100) * | ||
344 | + (1.2 + stats.criticalDamage / 100) * | ||
345 | + (1 - 3 * (1 - stats.ignoreGuard / 100)), | ||
346 | + 1); | ||
347 | +} | ||
348 | + | ||
202 | module.exports = { | 349 | module.exports = { |
203 | getCharacter: async function(req, res) { | 350 | getCharacter: async function(req, res) { |
204 | if (!req.query.nickname) { | 351 | if (!req.query.nickname) { |
... | @@ -225,8 +372,21 @@ module.exports = { | ... | @@ -225,8 +372,21 @@ module.exports = { |
225 | res.status(403).send(); | 372 | res.status(403).send(); |
226 | return; | 373 | return; |
227 | } | 374 | } |
228 | - console.log(analysisEquipment); | ||
229 | 375 | ||
230 | - res.send(characterInfo); | 376 | + const stats = analyzeStats(characterInfo, analysisEquipment); |
377 | + const buffStats = getBuffStats(stats, characterInfo.character.job); | ||
378 | + const efficiency = calculateEfficiency(stats, characterInfo.character.job, analysisEquipment.weapon); | ||
379 | + const buffEfficiency = calculateEfficiency(buffStats, characterInfo.character.job, analysisEquipment.weapon); | ||
380 | + | ||
381 | + res.send({ | ||
382 | + default: { | ||
383 | + stats: stats, | ||
384 | + efficiency: efficiency | ||
385 | + }, | ||
386 | + buff: { | ||
387 | + stats: buffStats, | ||
388 | + efficiency: buffEfficiency | ||
389 | + } | ||
390 | + }); | ||
231 | } | 391 | } |
232 | }; | 392 | }; |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
-
Please register or login to post a comment