NpcViewMod.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. /*
  2. * Copyright (C) 2004-2015 L2J DataPack
  3. *
  4. * This file is part of L2J DataPack.
  5. *
  6. * L2J DataPack is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * L2J DataPack is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package handlers.bypasshandlers;
  20. import java.text.DecimalFormat;
  21. import java.util.List;
  22. import java.util.Map;
  23. import java.util.StringTokenizer;
  24. import java.util.concurrent.TimeUnit;
  25. import com.l2jserver.gameserver.cache.HtmCache;
  26. import com.l2jserver.gameserver.datatables.ItemTable;
  27. import com.l2jserver.gameserver.handler.IBypassHandler;
  28. import com.l2jserver.gameserver.model.Elementals;
  29. import com.l2jserver.gameserver.model.L2Object;
  30. import com.l2jserver.gameserver.model.L2Spawn;
  31. import com.l2jserver.gameserver.model.L2World;
  32. import com.l2jserver.gameserver.model.actor.L2Character;
  33. import com.l2jserver.gameserver.model.actor.L2Npc;
  34. import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
  35. import com.l2jserver.gameserver.model.drops.DropListScope;
  36. import com.l2jserver.gameserver.model.drops.GeneralDropItem;
  37. import com.l2jserver.gameserver.model.drops.GroupedGeneralDropItem;
  38. import com.l2jserver.gameserver.model.drops.IDropItem;
  39. import com.l2jserver.gameserver.model.items.L2Item;
  40. import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage;
  41. import com.l2jserver.gameserver.util.HtmlUtil;
  42. import com.l2jserver.gameserver.util.Util;
  43. /**
  44. * @author NosBit
  45. */
  46. public class NpcViewMod implements IBypassHandler
  47. {
  48. private static final String[] COMMANDS =
  49. {
  50. "NpcViewMod"
  51. };
  52. private static final int DROP_LIST_ITEMS_PER_PAGE = 10;
  53. @Override
  54. public boolean useBypass(String command, L2PcInstance activeChar, L2Character bypassOrigin)
  55. {
  56. final StringTokenizer st = new StringTokenizer(command);
  57. st.nextToken();
  58. if (!st.hasMoreTokens())
  59. {
  60. _log.warning("Bypass[NpcViewMod] used without enough parameters.");
  61. return false;
  62. }
  63. final String actualCommand = st.nextToken();
  64. switch (actualCommand.toLowerCase())
  65. {
  66. case "view":
  67. {
  68. final L2Object target;
  69. if (st.hasMoreElements())
  70. {
  71. try
  72. {
  73. target = L2World.getInstance().findObject(Integer.parseInt(st.nextToken()));
  74. }
  75. catch (NumberFormatException e)
  76. {
  77. return false;
  78. }
  79. }
  80. else
  81. {
  82. target = activeChar.getTarget();
  83. }
  84. final L2Npc npc = target instanceof L2Npc ? (L2Npc) target : null;
  85. if (npc == null)
  86. {
  87. return false;
  88. }
  89. NpcViewMod.sendNpcView(activeChar, npc);
  90. break;
  91. }
  92. case "droplist":
  93. {
  94. if (st.countTokens() < 2)
  95. {
  96. _log.warning("Bypass[NpcViewMod] used without enough parameters.");
  97. return false;
  98. }
  99. final String dropListScopeString = st.nextToken();
  100. try
  101. {
  102. final DropListScope dropListScope = Enum.valueOf(DropListScope.class, dropListScopeString);
  103. final L2Object target = L2World.getInstance().findObject(Integer.parseInt(st.nextToken()));
  104. final L2Npc npc = target instanceof L2Npc ? (L2Npc) target : null;
  105. if (npc == null)
  106. {
  107. return false;
  108. }
  109. final int page = st.hasMoreElements() ? Integer.parseInt(st.nextToken()) : 0;
  110. sendNpcDropList(activeChar, npc, dropListScope, page);
  111. }
  112. catch (NumberFormatException e)
  113. {
  114. return false;
  115. }
  116. catch (IllegalArgumentException e)
  117. {
  118. _log.warning("Bypass[NpcViewMod] unknown drop list scope: " + dropListScopeString);
  119. return false;
  120. }
  121. break;
  122. }
  123. }
  124. return true;
  125. }
  126. @Override
  127. public String[] getBypassList()
  128. {
  129. return COMMANDS;
  130. }
  131. public static void sendNpcView(L2PcInstance activeChar, L2Npc npc)
  132. {
  133. final NpcHtmlMessage html = new NpcHtmlMessage();
  134. html.setFile(activeChar.getHtmlPrefix(), "data/html/mods/NpcView/Info.htm");
  135. html.replace("%name%", npc.getName());
  136. html.replace("%hpGauge%", HtmlUtil.getHpGauge(250, (long) npc.getCurrentHp(), npc.getMaxHp(), false));
  137. html.replace("%mpGauge%", HtmlUtil.getMpGauge(250, (long) npc.getCurrentMp(), npc.getMaxMp(), false));
  138. final L2Spawn npcSpawn = npc.getSpawn();
  139. if ((npcSpawn == null) || (npcSpawn.getRespawnMinDelay() == 0))
  140. {
  141. html.replace("%respawn%", "None");
  142. }
  143. else
  144. {
  145. TimeUnit timeUnit = TimeUnit.MILLISECONDS;
  146. long min = Long.MAX_VALUE;
  147. for (TimeUnit tu : TimeUnit.values())
  148. {
  149. final long minTimeFromMillis = tu.convert(npcSpawn.getRespawnMinDelay(), TimeUnit.MILLISECONDS);
  150. final long maxTimeFromMillis = tu.convert(npcSpawn.getRespawnMaxDelay(), TimeUnit.MILLISECONDS);
  151. if ((TimeUnit.MILLISECONDS.convert(minTimeFromMillis, tu) == npcSpawn.getRespawnMinDelay()) && (TimeUnit.MILLISECONDS.convert(maxTimeFromMillis, tu) == npcSpawn.getRespawnMaxDelay()))
  152. {
  153. if (min > minTimeFromMillis)
  154. {
  155. min = minTimeFromMillis;
  156. timeUnit = tu;
  157. }
  158. }
  159. }
  160. final long minRespawnDelay = timeUnit.convert(npcSpawn.getRespawnMinDelay(), TimeUnit.MILLISECONDS);
  161. final long maxRespawnDelay = timeUnit.convert(npcSpawn.getRespawnMaxDelay(), TimeUnit.MILLISECONDS);
  162. final String timeUnitName = timeUnit.name().charAt(0) + timeUnit.name().toLowerCase().substring(1);
  163. if (npcSpawn.hasRespawnRandom())
  164. {
  165. html.replace("%respawn%", minRespawnDelay + "-" + maxRespawnDelay + " " + timeUnitName);
  166. }
  167. else
  168. {
  169. html.replace("%respawn%", minRespawnDelay + " " + timeUnitName);
  170. }
  171. }
  172. html.replace("%atktype%", Util.capitalizeFirst(npc.getAttackType().name().toLowerCase()));
  173. html.replace("%atkrange%", npc.getStat().getPhysicalAttackRange());
  174. html.replace("%patk%", npc.getPAtk(activeChar));
  175. html.replace("%pdef%", npc.getPDef(activeChar));
  176. html.replace("%matk%", npc.getMAtk(activeChar, null));
  177. html.replace("%mdef%", npc.getMDef(activeChar, null));
  178. html.replace("%atkspd%", npc.getPAtkSpd());
  179. html.replace("%castspd%", npc.getMAtkSpd());
  180. html.replace("%critrate%", npc.getStat().getCriticalHit(activeChar, null));
  181. html.replace("%evasion%", npc.getEvasionRate(activeChar));
  182. html.replace("%accuracy%", npc.getStat().getAccuracy());
  183. html.replace("%speed%", (int) npc.getStat().getMoveSpeed());
  184. html.replace("%attributeatktype%", Elementals.getElementName(npc.getStat().getAttackElement()));
  185. html.replace("%attributeatkvalue%", npc.getStat().getAttackElementValue(npc.getStat().getAttackElement()));
  186. html.replace("%attributefire%", npc.getStat().getDefenseElementValue(Elementals.FIRE));
  187. html.replace("%attributewater%", npc.getStat().getDefenseElementValue(Elementals.WATER));
  188. html.replace("%attributewind%", npc.getStat().getDefenseElementValue(Elementals.WIND));
  189. html.replace("%attributeearth%", npc.getStat().getDefenseElementValue(Elementals.EARTH));
  190. html.replace("%attributedark%", npc.getStat().getDefenseElementValue(Elementals.DARK));
  191. html.replace("%attributeholy%", npc.getStat().getDefenseElementValue(Elementals.HOLY));
  192. html.replace("%dropListButtons%", getDropListButtons(npc));
  193. activeChar.sendPacket(html);
  194. }
  195. public static String getDropListButtons(L2Npc npc)
  196. {
  197. final StringBuilder sb = new StringBuilder();
  198. final Map<DropListScope, List<IDropItem>> dropLists = npc.getTemplate().getDropLists();
  199. if ((dropLists != null) && !dropLists.isEmpty() && (dropLists.containsKey(DropListScope.DEATH) || dropLists.containsKey(DropListScope.CORPSE)))
  200. {
  201. sb.append("<table width=275 cellpadding=0 cellspacing=0><tr>");
  202. if (dropLists.containsKey(DropListScope.DEATH))
  203. {
  204. sb.append("<td align=center><button value=\"Show Drop\" width=100 height=25 action=\"bypass NpcViewMod dropList DEATH " + npc.getObjectId() + "\" back=\"L2UI_CT1.Button_DF_Calculator_Down\" fore=\"L2UI_CT1.Button_DF_Calculator\"></td>");
  205. }
  206. if (dropLists.containsKey(DropListScope.CORPSE))
  207. {
  208. sb.append("<td align=center><button value=\"Show Spoil\" width=100 height=25 action=\"bypass NpcViewMod dropList CORPSE " + npc.getObjectId() + "\" back=\"L2UI_CT1.Button_DF_Calculator_Down\" fore=\"L2UI_CT1.Button_DF_Calculator\"></td>");
  209. }
  210. sb.append("</tr></table>");
  211. }
  212. return sb.toString();
  213. }
  214. public static void sendNpcDropList(L2PcInstance activeChar, L2Npc npc, DropListScope dropListScope, int page)
  215. {
  216. final List<IDropItem> dropList = npc.getTemplate().getDropList(dropListScope);
  217. if ((dropList == null) || dropList.isEmpty())
  218. {
  219. return;
  220. }
  221. int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE;
  222. if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size())
  223. {
  224. pages++;
  225. }
  226. final StringBuilder pagesSb = new StringBuilder();
  227. if (pages > 1)
  228. {
  229. pagesSb.append("<table><tr>");
  230. for (int i = 0; i < pages; i++)
  231. {
  232. pagesSb.append("<td align=center><button value=\"" + (i + 1) + "\" width=20 height=20 action=\"bypass NpcViewMod dropList " + dropListScope + " " + npc.getObjectId() + " " + i + "\" back=\"L2UI_CT1.Button_DF_Calculator_Down\" fore=\"L2UI_CT1.Button_DF_Calculator\"></td>");
  233. }
  234. pagesSb.append("</tr></table>");
  235. }
  236. if (page >= pages)
  237. {
  238. page = pages - 1;
  239. }
  240. final int start = page > 0 ? page * DROP_LIST_ITEMS_PER_PAGE : 0;
  241. int end = (page * DROP_LIST_ITEMS_PER_PAGE) + DROP_LIST_ITEMS_PER_PAGE;
  242. if (end > dropList.size())
  243. {
  244. end = dropList.size();
  245. }
  246. final DecimalFormat amountFormat = new DecimalFormat("#,###");
  247. final DecimalFormat chanceFormat = new DecimalFormat("0.00##");
  248. int leftHeight = 0;
  249. int rightHeight = 0;
  250. final StringBuilder leftSb = new StringBuilder();
  251. final StringBuilder rightSb = new StringBuilder();
  252. for (int i = start; i < end; i++)
  253. {
  254. final StringBuilder sb = new StringBuilder();
  255. int height = 64;
  256. final IDropItem dropItem = dropList.get(i);
  257. if (dropItem instanceof GeneralDropItem)
  258. {
  259. addGeneralDropItem(activeChar, npc, amountFormat, chanceFormat, sb, (GeneralDropItem) dropItem);
  260. }
  261. else if (dropItem instanceof GroupedGeneralDropItem)
  262. {
  263. final GroupedGeneralDropItem generalGroupedDropItem = (GroupedGeneralDropItem) dropItem;
  264. if (generalGroupedDropItem.getItems().size() == 1)
  265. {
  266. final GeneralDropItem generalDropItem = generalGroupedDropItem.getItems().get(0);
  267. addGeneralDropItem(activeChar, npc, amountFormat, chanceFormat, sb, new GeneralDropItem(generalDropItem.getItemId(), generalDropItem.getMin(), generalDropItem.getMax(), (generalDropItem.getChance() * generalGroupedDropItem.getChance()) / 100, generalDropItem.getAmountStrategy(), generalDropItem.getChanceStrategy(), generalGroupedDropItem.getPreciseStrategy(), generalGroupedDropItem.getKillerChanceModifierStrategy(), generalDropItem.getDropCalculationStrategy()));
  268. }
  269. else
  270. {
  271. GroupedGeneralDropItem normalized = generalGroupedDropItem.normalizeMe(npc, activeChar);
  272. sb.append("<table width=332 cellpadding=2 cellspacing=0 background=\"L2UI_CT1.Windows.Windows_DF_TooltipBG\">");
  273. sb.append("<tr><td width=32 valign=top><img src=\"L2UI_CT1.ICON_DF_premiumItem\" width=32 height=32></td>");
  274. sb.append("<td fixwidth=300 align=center><font name=\"ScreenMessageSmall\" color=\"CD9000\">One from group</font>");
  275. sb.append("</td></tr><tr><td width=32></td><td width=300><table width=295 cellpadding=0 cellspacing=0><tr>");
  276. sb.append("<td width=48 align=right valign=top><font color=\"LEVEL\">Chance:</font></td>");
  277. sb.append("<td width=247 align=center>");
  278. sb.append(chanceFormat.format(Math.min(normalized.getChance(), 100)));
  279. sb.append("%</td></tr></table><br>");
  280. for (GeneralDropItem generalDropItem : normalized.getItems())
  281. {
  282. final L2Item item = ItemTable.getInstance().getTemplate(generalDropItem.getItemId());
  283. sb.append("<table width=291 cellpadding=2 cellspacing=0 background=\"L2UI_CT1.Windows.Windows_DF_TooltipBG\">");
  284. sb.append("<tr><td width=32 valign=top>");
  285. sb.append("<img src=\"" + item.getIcon() + "\" width=32 height=32>");
  286. sb.append("</td><td fixwidth=259 align=center><font name=\"hs9\" color=\"CD9000\">");
  287. sb.append(item.getName());
  288. sb.append("</font></td></tr><tr><td width=32></td><td width=259><table width=253 cellpadding=0 cellspacing=0>");
  289. sb.append("<tr><td width=48 align=right valign=top><font color=\"LEVEL\">Amount:</font></td><td width=205 align=center>");
  290. MinMax minMax = getPreciseMinMax(normalized.getChance(), generalDropItem.getMin(npc), generalDropItem.getMax(npc), generalDropItem.isPreciseCalculated());
  291. final long min = minMax.min;
  292. final long max = minMax.max;
  293. if (min == max)
  294. {
  295. sb.append(amountFormat.format(min));
  296. }
  297. else
  298. {
  299. sb.append(amountFormat.format(min));
  300. sb.append(" - ");
  301. sb.append(amountFormat.format(max));
  302. }
  303. sb.append("</td></tr><tr><td width=48 align=right valign=top><font color=\"LEVEL\">Chance:</font></td>");
  304. sb.append("<td width=205 align=center>");
  305. sb.append(chanceFormat.format(Math.min(generalDropItem.getChance(), 100)));
  306. sb.append("%</td></tr></table></td></tr><tr><td width=32></td><td width=259>&nbsp;</td></tr></table>");
  307. height += 64;
  308. }
  309. sb.append("</td></tr><tr><td width=32></td><td width=300>&nbsp;</td></tr></table>");
  310. }
  311. }
  312. if (leftHeight >= (rightHeight + height))
  313. {
  314. rightSb.append(sb);
  315. rightHeight += height;
  316. }
  317. else
  318. {
  319. leftSb.append(sb);
  320. leftHeight += height;
  321. }
  322. }
  323. final StringBuilder bodySb = new StringBuilder();
  324. bodySb.append("<table><tr>");
  325. bodySb.append("<td>");
  326. bodySb.append(leftSb.toString());
  327. bodySb.append("</td><td>");
  328. bodySb.append(rightSb.toString());
  329. bodySb.append("</td>");
  330. bodySb.append("</tr></table>");
  331. String html = HtmCache.getInstance().getHtm(activeChar.getHtmlPrefix(), "data/html/mods/NpcView/DropList.htm");
  332. if (html == null)
  333. {
  334. _log.warning(NpcViewMod.class.getSimpleName() + ": The html file data/html/mods/NpcView/DropList.htm could not be found.");
  335. return;
  336. }
  337. html = html.replaceAll("%name%", npc.getName());
  338. html = html.replaceAll("%dropListButtons%", getDropListButtons(npc));
  339. html = html.replaceAll("%pages%", pagesSb.toString());
  340. html = html.replaceAll("%items%", bodySb.toString());
  341. Util.sendCBHtml(activeChar, html);
  342. }
  343. /**
  344. * @param activeChar
  345. * @param npc
  346. * @param amountFormat
  347. * @param chanceFormat
  348. * @param sb
  349. * @param dropItem
  350. */
  351. private static void addGeneralDropItem(L2PcInstance activeChar, L2Npc npc, final DecimalFormat amountFormat, final DecimalFormat chanceFormat, final StringBuilder sb, final GeneralDropItem dropItem)
  352. {
  353. final L2Item item = ItemTable.getInstance().getTemplate(dropItem.getItemId());
  354. sb.append("<table width=332 cellpadding=2 cellspacing=0 background=\"L2UI_CT1.Windows.Windows_DF_TooltipBG\">");
  355. sb.append("<tr><td width=32 valign=top>");
  356. sb.append("<img src=\"" + item.getIcon() + "\" width=32 height=32>");
  357. sb.append("</td><td fixwidth=300 align=center><font name=\"hs9\" color=\"CD9000\">");
  358. sb.append(item.getName());
  359. sb.append("</font></td></tr><tr><td width=32></td><td width=300><table width=295 cellpadding=0 cellspacing=0>");
  360. sb.append("<tr><td width=48 align=right valign=top><font color=\"LEVEL\">Amount:</font></td>");
  361. sb.append("<td width=247 align=center>");
  362. MinMax minMax = getPreciseMinMax(dropItem.getChance(npc, activeChar), dropItem.getMin(npc), dropItem.getMax(npc), dropItem.isPreciseCalculated());
  363. final long min = minMax.min;
  364. final long max = minMax.max;
  365. if (min == max)
  366. {
  367. sb.append(amountFormat.format(min));
  368. }
  369. else
  370. {
  371. sb.append(amountFormat.format(min));
  372. sb.append(" - ");
  373. sb.append(amountFormat.format(max));
  374. }
  375. sb.append("</td></tr><tr><td width=48 align=right valign=top><font color=\"LEVEL\">Chance:</font></td>");
  376. sb.append("<td width=247 align=center>");
  377. sb.append(chanceFormat.format(Math.min(dropItem.getChance(npc, activeChar), 100)));
  378. sb.append("%</td></tr></table></td></tr><tr><td width=32></td><td width=300>&nbsp;</td></tr></table>");
  379. }
  380. private static class MinMax
  381. {
  382. public final long min, max;
  383. public MinMax(long min, long max)
  384. {
  385. this.min = min;
  386. this.max = max;
  387. }
  388. }
  389. private static MinMax getPreciseMinMax(double chance, long min, long max, boolean isPrecise)
  390. {
  391. if (!isPrecise || (chance <= 100))
  392. {
  393. return new MinMax(min, max);
  394. }
  395. int mult = (int) (chance) / 100;
  396. return new MinMax(mult * min, (chance % 100) > 0 ? (mult + 1) * max : mult * max);
  397. }
  398. }