diff --git a/app/mapping.js b/app/mapping.js index fbd7807e..442ba85e 100644 --- a/app/mapping.js +++ b/app/mapping.js @@ -1968,6 +1968,48 @@ module.exports = { max: (efficiency + ((Math.max(Math.ceil((12 - rune.upgrade_curr) / 3.0), 0) * 0.2) / 2.8) * 100).toFixed(toFixed), }; }, +getRuneEfficiencySWOP(rune, toFixed = 2) { + let ratio = 0.0; + + // main stat + ratio += + this.rune.mainstat[rune.pri_eff[0]].max[this.isAncient(rune) ? rune.class - 10 : rune.class] / this.rune.mainstat[rune.pri_eff[0]].max[6]; + + // sub stats + rune.sec_eff.forEach((stat) => { + let value = stat[3] && stat[3] > 0 ? stat[1] + stat[3] : stat[1]; + + if (this.isFlatStat(stat[0])) { + value *= 0.5; // Multiply flat stats by 0.5 + } + + ratio += value / this.rune.substat[stat[0]].max[6]; + }); + + // innate stat + if (rune.prefix_eff && rune.prefix_eff[0] > 0) { + let innateValue = rune.prefix_eff[1]; + + if (this.isFlatStat(rune.prefix_eff[0])) { + innateValue *= 0.5; // Multiply flat stats by 0.5 + } + + ratio += innateValue / this.rune.substat[rune.prefix_eff[0]].max[6]; + } + + let efficiency = (ratio / 2.8) * 100; + + return { + current: ((ratio / 2.8) * 100).toFixed(toFixed), + max: (efficiency + ((Math.max(Math.ceil((12 - rune.upgrade_curr) / 3.0), 0) * 0.2) / 2.8) * 100).toFixed(toFixed), + }; +}, +isFlatStat(statType) { + // List of stat types that are considered flat + const flatStats = [1, 3, 5]; // ATK flat, DEF flat, HP flat + + return flatStats.includes(statType); +}, getRuneEffect(eff) { const type = eff[0]; const value = eff[1]; diff --git a/app/plugins/rune-drop-efficiency-swop.js b/app/plugins/rune-drop-efficiency-swop.js new file mode 100644 index 00000000..14bbd9dd --- /dev/null +++ b/app/plugins/rune-drop-efficiency-swop.js @@ -0,0 +1,183 @@ +module.exports = { + defaultConfig: { + enabled: true, + }, + pluginName: 'RuneDropEfficiencySWOP', + pluginDescription: 'As per the updated SWOP formula: Logs the maximum possible efficiency for runes as they drop.', + init(proxy) { + proxy.on('apiCommand', (req, resp) => { + if (config.Config.Plugins[this.pluginName].enabled) { + this.processCommand(proxy, req, resp); + } + }); + }, + processCommand(proxy, req, resp) { + const { command } = req; + let runesInfo = []; + + // Extract the rune and display it's efficiency stats. + switch (command) { + case 'BattleDungeonResult': + case 'BattleScenarioResult': + case 'BattleDimensionHoleDungeonResult': + if (resp.win_lose === 1) { + const reward = resp.reward ? resp.reward : {}; + + if (reward.crate && reward.crate.rune) { + runesInfo.push(this.logRuneDrop(reward.crate.rune)); + } + } + break; + case 'BattleDungeonResult_V2': + if (resp.win_lose === 1) { + const rewards = resp.changed_item_list ? resp.changed_item_list : []; + + if (rewards) { + rewards.forEach((reward) => { + if (reward.type === 8) { + runesInfo.push(this.logRuneDrop(reward.info)); + } + }); + } + } + break; + case 'upgradeRune_v2': { + const newLevel = resp.rune.upgrade_curr; + + if (newLevel % 3 === 0 && newLevel <= 12) { + runesInfo.push(this.logRuneDrop(resp.rune)); + } + break; + } + case 'AmplifyRune': + case 'AmplifyRune_v2': + case 'ConvertRune': + case 'ConvertRune_v2': + case 'ConfirmRune': + runesInfo.push(this.logRuneDrop(resp.rune)); + break; + + case 'BuyBlackMarketItem': + if (resp.runes && resp.runes.length === 1) { + runesInfo.push(this.logRuneDrop(resp.runes[0])); + } + break; + + case 'BuyGuildBlackMarketItem': + if (resp.runes && resp.runes.length === 1) { + runesInfo.push(this.logRuneDrop(resp.runes[0])); + } + break; + + case 'BuyShopItem': + if (resp.reward && resp.reward.crate && resp.reward.crate.runes) { + runesInfo.push(this.logRuneDrop(resp.reward.crate.runes[0])); + } + break; + + case 'GetBlackMarketList': + resp.market_list.forEach((item) => { + if (item.item_master_type === 8 && item.runes) { + runesInfo.push(this.logRuneDrop(item.runes[0])); + } + }); + break; + + case 'GetGuildBlackMarketList': + resp.market_list.forEach((item) => { + if (item.item_master_type === 8 && item.runes) { + runesInfo.push(this.logRuneDrop(item.runes[0])); + } + }); + break; + + case 'BattleWorldBossResult': { + const reward = resp.reward ? resp.reward : {}; + + if (reward.crate && reward.crate.runes) { + reward.crate.runes.forEach((rune) => { + runesInfo.push(this.logRuneDrop(rune)); + }); + } + break; + } + case 'BattleRiftDungeonResult': + if (resp.item_list) { + resp.item_list.forEach((item) => { + if (item.type === 8) { + runesInfo.push(this.logRuneDrop(item.info)); + } + }); + } + break; + + case 'RevalueRune': + runesInfo.push('New rune efficiency value:' + this.logRuneDrop(resp.rune)); + + default: + break; + } + + if (runesInfo.length > 0) { + proxy.log({ + type: 'info', + source: 'plugin', + name: this.pluginName, + message: this.mountRuneListHtml(runesInfo), + }); + } + }, + + logRuneDrop(rune) { + const efficiency = gMapping.getRuneEfficiencySWOP(rune); + const runeQuality = gMapping.rune.quality[rune.rank]; + const colorTable = { + Common: 'grey', + Magic: 'green', + Rare: 'blue', + Hero: 'purple', + Legend: 'orange', + }; + + let color = colorTable[runeQuality]; + let starHtml = this.mountStarsHtml(rune); + + return `
+
+ + +${rune.upgrade_curr} +
+ +
+ ${starHtml} +
${gMapping.isAncient(rune) ? 'Ancient ' : ''}${gMapping.rune.sets[rune.set_id]} Rune (${rune.slot_no}) ${ + gMapping.rune.effectTypes[rune.pri_eff[0]] + }
+
Efficiency: ${efficiency.current}%. ${rune.upgrade_curr < 12 ? `Max: ${efficiency.max}%` : ''}
+
+
`; + }, + + mountStarsHtml(rune) { + let count = 0; + let html = '
'; + let runeClass = gMapping.isAncient(rune) ? rune.class - 10 : rune.class; + while (count < runeClass) { + html = html.concat(''); + count += 1; + } + + return html.concat('
'); + }, + + mountRuneListHtml(runes) { + let message = '
'; + + runes.forEach((rune) => { + message = message.concat(rune); + }); + + return message.concat('
'); + }, + }; + \ No newline at end of file