1. //Version 5
  2.  
  3. import javax.swing.JApplet;
  4. import javax.swing.JPanel;
  5. import javax.swing.JTabbedPane;
  6. import javax.swing.JLabel;
  7. import javax.swing.JFormattedTextField;
  8. import javax.swing.border.Border;
  9. import javax.swing.BorderFactory;
  10. import javax.swing.JTextArea;
  11. import javax.swing.JCheckBox;
  12.  
  13. import java.awt.Dimension;
  14. import java.awt.GridLayout;
  15. import java.awt.Color;
  16. import java.awt.GridBagLayout;
  17. import java.awt.GridBagConstraints;
  18. import java.awt.event.ItemListener;
  19. import java.awt.event.ItemEvent;
  20. //import java.awt.Insets;
  21.  
  22. import java.beans.PropertyChangeListener;
  23. import java.beans.PropertyChangeEvent;
  24. import java.text.NumberFormat;
  25.  
  26. import org.jfree.chart.JFreeChart;
  27. import org.jfree.chart.ChartFactory;
  28. import org.jfree.chart.ChartPanel;
  29. import org.jfree.chart.axis.NumberAxis;
  30. import org.jfree.chart.plot.PlotOrientation;
  31. import org.jfree.chart.plot.XYPlot;
  32. import org.jfree.data.xy.XYDataset;
  33. import org.jfree.data.xy.XYSeries;
  34. import org.jfree.data.xy.XYSeriesCollection;
  35. import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
  36.  
  37. public class VDWRShamModeler extends JApplet implements PropertyChangeListener, ItemListener
  38. {
  39. public static double intellect = 1489; //base intellect
  40. public static double spellpower = 3301;
  41. public static double critrating = 689;
  42. public static double hasterating = 1269;
  43. public static double stacks = 50;
  44. public static final double classcrit = 5 + 4 + 2.2; //percent chance to get crits from class and global crit talents (
  45.  
  46. static boolean Kings = true; //10% to all stats
  47. static boolean WrathOfAir = true; //5% spell haste
  48. static boolean MoonkinAura = true; //3% haste, 5% crit
  49. static boolean Berserking = false; //Troll racial, 20% haste
  50. static boolean Bloodlust = false; //30% haste, but does not affect GCD
  51. static boolean TreeOfLife = true; //6% additional healing done
  52. static boolean ICCBuff = true; //25% additional healing done
  53. static boolean MOTW = true; //Increases all atributes by 37
  54. static boolean ForKings = false; //8% to all stats
  55.  
  56. static XYSeries RHHCHHseries;
  57. static XYSeries RHHHHseries;
  58. static XYSeries RHHHLseries;
  59. static XYSeries HHHHHseries;
  60.  
  61. static JFormattedTextField intellectField;
  62. static JFormattedTextField spellpowerField;
  63. static JFormattedTextField critratingField;
  64. static JFormattedTextField hasteratingField;
  65. static JFormattedTextField stacksField;
  66.  
  67. static JCheckBox KingsBox;
  68. static JCheckBox WrathOfAirBox;
  69. static JCheckBox MoonkinAuraBox;
  70. static JCheckBox BerserkingBox;
  71. static JCheckBox BloodlustBox;
  72. static JCheckBox TreeOfLifeBox;
  73. static JCheckBox ICCBuffBox;
  74. static JCheckBox MOTWBox;
  75. static JCheckBox ForKingsBox;
  76.  
  77. static JTextArea textArea;
  78.  
  79.  
  80. //Called when this applet is loaded into the browser
  81. public void init()
  82. {
  83. setSize(new Dimension(800,600));
  84. XYDataset dataset = createDataset();
  85. JFreeChart hpsChart = createChart(dataset);
  86. ChartPanel chartPanel = new ChartPanel(hpsChart);
  87.  
  88. JPanel statsPanel = createStatsPanel();
  89.  
  90. JTabbedPane tabs = new JTabbedPane();
  91. tabs.addTab("Graph", chartPanel);
  92. tabs.addTab("Stats", statsPanel);
  93.  
  94. tabs.setPreferredSize(new Dimension(1920, 1200));
  95. setContentPane(tabs);
  96. simulate();
  97. }
  98.  
  99. public void simulate()
  100. {
  101. /* base healing amounts (non-crit) before non-character buffs */
  102. double LHWbase = 1.2 * 1.1 * (1720 + spellpower * 0.9057); //I assume if you are going to use LHW you better have ES on the target and ES glyph
  103. double HWbase = 1.25 * 1.1 * (3250 + spellpower * 1.8114);
  104. double CHbase = 1.2 * 1.1 * (1130 + spellpower * 1.3428);
  105. double RTbase = 1.1 * (1670 + spellpower * 0.402);
  106. double RThot = 1.1 * (334 + spellpower * 0.188);
  107. double ELhot = 1.1 * (652 + spellpower * 12/15 * 1.88 * 5/11) / 4;
  108.  
  109. double critchance = (critrating / 45.91 + classcrit + intellect / 166.6667) / 100;
  110. double hastemultiplier = 1 / (1 + hasterating / 3279);
  111. double AAcoeff = 0.297;
  112. double GCD = 1.5;
  113.  
  114. /* various buffs (comment out the line if you don't want those buffs calculated; bloodlust is located after GCD calculation) */
  115. if (MOTW) { intellect += 37; } //MOTW
  116. if (Kings) { critchance += 0.1 * intellect / 166.6667 / 100; } //Kings
  117. if (ForKings) { critchance += 0.08 * intellect / 166.6667 / 100; } //Blessing of Forgotten Kings
  118. if (WrathOfAir) { hastemultiplier *= 1 / (1 + 5.0/100); } //WoA
  119. if (MoonkinAura) { hastemultiplier *= 1 / (1 + 3.0/100); critchance += 0.05; } //Moonkin aura
  120. if (Berserking) { hastemultiplier *= 1 / (1 + 20.0/100); } //Berserking
  121. if (TreeOfLife) { LHWbase *= 1.06; HWbase *= 1.06; CHbase *= 1.06; RTbase *= 1.06; RThot *= 1.06; ELhot *= 1.06; AAcoeff *= 1.06; } //Tree of Life/Imp Devo
  122. if (ICCBuff) { LHWbase *= 1.25; HWbase *= 1.25; CHbase *= 1.25; RTbase *= 1.25; RThot *= 1.25; ELhot *= 1.25; AAcoeff *= 1.25; } //ICC 25% buff
  123.  
  124. GCD = GCD * hastemultiplier >= 1 ? GCD * hastemultiplier : 1.0;
  125. if (Bloodlust) { hastemultiplier *= 1 / (1 + 30.0/100); } // bloodlust; doesn't affect GCD but can be modeled as effective haste, so it is added in after GCD is calculated
  126.  
  127. textArea.setText("LHW base heal (w/ ES on the target):\t" + LHWbase);
  128. textArea.append("\nHW base heal:\t\t\t" + HWbase);
  129. textArea.append("\nCH base heal:\t\t\t" + CHbase);
  130. textArea.append("\nRT base heal:\t\t\t" + RTbase);
  131. textArea.append("\nRT tick:\t\t\t" + RThot);
  132. textArea.append("\nEL tick:\t\t\t" + ELhot);
  133.  
  134. textArea.append("\n\nCrit chance:\t\t\t" + critchance * 100 + "%");
  135. textArea.append("\nHaste multiplier:\t\t" + hastemultiplier);
  136.  
  137. double RTtime = 1.5 * hastemultiplier >= GCD ? 1.5 * hastemultiplier : GCD; //checks to see if spells are at or under the GCD, assigns 1 sec if they are
  138. double LHWtime = 1.5 * hastemultiplier >= GCD ? 1.5 * hastemultiplier : GCD;
  139. double CHtime = 2.5 * hastemultiplier >= GCD ? 2.5 * hastemultiplier : GCD;
  140. double HWtime = 2.5 * hastemultiplier >= GCD ? 2.5 * hastemultiplier : GCD;
  141. double HWTWtime = 2.5 * .7 * hastemultiplier >= 1 ? 2.5 * .7 * hastemultiplier : 1;
  142. double HWTW2pctime = HWTWtime * 1 / (1 + 20.0/100) >= 1 ? HWTWtime * 1 / (1 + 20.0/100) : 1;
  143.  
  144. System.out.println("RT cast time:\t\t\t" + RTtime);
  145. System.out.println("LHW cast time:\t\t\t" + LHWtime);
  146. System.out.println("CH time:\t\t\t" + CHtime);
  147. System.out.println("HW cast time:\t\t\t" + HWtime);
  148. System.out.println("HW w/ Tidal Waves cast time:\t" + HWTWtime);
  149. System.out.println("HW w/ Tidal Waves + 2pct10:\t" + HWTW2pctime);
  150. System.out.println();
  151.  
  152. double RHHCHHtime = RTtime + //RT
  153. HWTW2pctime + //the HW after the RT due to 2pcT10
  154. HWTWtime * 3 + //the other 3 HW
  155. CHtime; //CH
  156. if (RHHCHHtime < 6) RHHCHHtime = 6;
  157.  
  158. double RHHHHtime = RTtime +
  159. HWTW2pctime +
  160. HWTWtime + //the next HW
  161. HWtime * 2; //the next 2 HW
  162. if (RHHHHtime < 6) RHHHHtime = 6;
  163.  
  164. double RHHHLtime = RTtime +
  165. HWTW2pctime +
  166. HWTWtime + //the next HW
  167. HWtime + //the next HW
  168. LHWtime;
  169. if (RHHHLtime < 6) RHHHLtime = 6;
  170.  
  171. double RHHHHHtime = RTtime +
  172. HWTW2pctime +
  173. HWTWtime + //the next HW
  174. HWtime * 3; //the next 3 HW
  175. if (RHHHHHtime < 6) RHHHHHtime = 6;
  176.  
  177. double HHHHHtime = HWtime * 5; //5 HW
  178.  
  179. textArea.append("\n\nRHHCHH rotation time:\t\t" + RHHCHHtime);
  180. textArea.append("\nRHHHH rotation time:\t\t" + RHHHHtime);
  181. //System.out.println("RHHHHH rotation time:\t\t" + RHHHHHtime);
  182. textArea.append("\nRHHHL rotation time:\t\t" + RHHHLtime);
  183. textArea.append("\nHHHHH rotation time:\t\t" + HHHHHtime);
  184. //System.out.println();
  185.  
  186. long crossoverStacks = 0;
  187.  
  188. RHHCHHseries.clear();
  189. RHHHHseries.clear();
  190. RHHHLseries.clear();
  191. HHHHHseries.clear();
  192.  
  193. double RHHCHHavg = 0;
  194. double RHHHHavg = 0;
  195. double RHHHLavg = 0;
  196. double RHHHHHavg = 0;
  197. double HHHHHavg = 0;
  198.  
  199. for (int i = 0; i <= stacks; i++)
  200. {
  201. double stacksMultiplier = 1 + 0.1 * i;
  202. double HWavg = stacksMultiplier * (HWbase * (1 - critchance) + HWbase * critchance * 1.5 + HWbase * critchance * 1.5 * AAcoeff * stacksMultiplier);
  203. double LHWavg = stacksMultiplier * (LHWbase * (1 - critchance) + LHWbase * critchance * 1.5 + LHWbase * critchance * 1.5 * AAcoeff * stacksMultiplier);
  204. double CHavg = stacksMultiplier * (CHbase * (1 - critchance) + CHbase * critchance * 1.5 + CHbase * 1.5 * critchance * .25); //4 pc T10 Rsham
  205. double RTavg = stacksMultiplier * (RTbase * (1 - critchance) + RTbase * critchance * 1.5 + RTbase * critchance * 1.5 * AAcoeff);
  206. double RTtickavg = stacksMultiplier * RThot;
  207. double ELhotavg = stacksMultiplier * ELhot;
  208.  
  209. RHHCHHavg = RTavg + RTtickavg * Math.floor((RTtime + HWTW2pctime + HWTWtime) / 3) + HWavg * 4 + 1.25 * CHavg;
  210. RHHHHavg = RTavg + RTtickavg * Math.floor((RHHHHtime) / 3) + HWavg * 2 + HWavg * 2;
  211. RHHHLavg = RTavg + RTtickavg * Math.floor((RHHHLtime) / 3) + HWavg * 2 + HWavg + LHWavg;
  212. RHHHHHavg = RTavg + RTtickavg * Math.floor((RHHHHHtime) / 3) + HWavg * 2 + HWavg * 3;
  213. HHHHHavg = HWavg * 5;
  214.  
  215. RHHCHHseries.add(i, RHHCHHavg/RHHCHHtime);
  216. RHHHHseries.add(i, RHHHHavg/RHHHHtime);
  217. RHHHLseries.add(i, RHHHLavg/RHHHLtime);
  218. HHHHHseries.add(i, HHHHHavg/HHHHHtime);
  219.  
  220. /*System.out.println();
  221. System.out.println("Stacks:\t" + i);
  222. System.out.println("RHHCHH HPS:\t" + RHHCHHavg/RHHCHHtime);
  223. System.out.println("RHHHH HPS:\t" + RHHHHavg/RHHHHtime);*/
  224. //System.out.println("RHHHL HPS:\t" + RHHHLavg/RHHHLtime);
  225. //System.out.println("RHHHHH HPS:\t" + RHHHHHavg/RHHHHHtime);
  226. //System.out.println("HHHHH HPS:\t" + HHHHHavg/HHHHHtime);
  227. //System.out.println("% difference:\t" + (RHHHHavg/RHHHHtime - RHHCHHavg/RHHCHHtime) / (RHHCHHavg/RHHCHHtime) * 100 + "%");
  228.  
  229. if (RHHHHavg/RHHHHtime > RHHCHHavg/RHHCHHtime && crossoverStacks == 0)
  230. crossoverStacks = i;
  231. }
  232.  
  233. if (RHHHHavg/RHHHHtime > RHHCHHavg/RHHCHHtime)
  234. textArea.append("\n\nRHHHH overtakes RHHCHH at " + crossoverStacks + " stacks");
  235. else
  236. textArea.append("\n\nRHHHH never overtakes RHHCHH, please extend simulated stacks");
  237. }
  238.  
  239. public XYDataset createDataset()
  240. {
  241. RHHCHHseries = new XYSeries("RHHCHH");
  242. RHHHHseries = new XYSeries("RHHHH");
  243. RHHHLseries = new XYSeries("RHHHL");
  244. HHHHHseries = new XYSeries("Healing Wave Spam");
  245. XYSeriesCollection dataset = new XYSeriesCollection();
  246. dataset.addSeries(RHHCHHseries);
  247. dataset.addSeries(RHHHHseries);
  248. dataset.addSeries(RHHHLseries);
  249. dataset.addSeries(HHHHHseries);
  250.  
  251. return dataset;
  252.  
  253. }
  254.  
  255. public JFreeChart createChart(XYDataset dataset)
  256. {
  257. JFreeChart chart = ChartFactory.createXYLineChart(
  258. "HPS vs Stacks", // chart title
  259. "Stacks", // x axis label
  260. "HPS", // y axis label
  261. dataset, // data
  262. PlotOrientation.VERTICAL,
  263. true, // include legend
  264. true, // tooltips
  265. false // urls
  266. );
  267.  
  268. // get a reference to the plot for further customisation...
  269. XYPlot plot = (XYPlot) chart.getPlot();
  270.  
  271. XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer();
  272. renderer.setBaseShapesVisible(false);
  273. renderer.setBaseShapesFilled(false);
  274.  
  275. // change the auto tick unit selection to integer units only...
  276. NumberAxis domainAxis = (NumberAxis) plot.getDomainAxis();
  277. NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
  278. domainAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
  279. rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
  280.  
  281. return chart;
  282. }
  283.  
  284. public JPanel createStatsPanel()
  285. {
  286. //create new labels
  287. JLabel intellectLabel = new JLabel("Intellect");
  288. JLabel spellpowerLabel = new JLabel("Spellpower");
  289. JLabel critratingLabel = new JLabel("Crit Rating");
  290. JLabel hasteratingLabel = new JLabel("Haste Rating");
  291. JLabel stacksLabel = new JLabel("Number of stacks to simulate");
  292.  
  293. //create new fields
  294. intellectField = new JFormattedTextField(NumberFormat.getNumberInstance());
  295. intellectField.setValue(intellect);
  296. intellectField.setColumns(10);
  297. intellectField.addPropertyChangeListener("value", this);
  298.  
  299. spellpowerField = new JFormattedTextField(NumberFormat.getNumberInstance());
  300. spellpowerField.setValue(spellpower);
  301. spellpowerField.setColumns(10);
  302. spellpowerField.addPropertyChangeListener("value", this);
  303.  
  304. critratingField = new JFormattedTextField(NumberFormat.getNumberInstance());
  305. critratingField.setValue(critrating);
  306. critratingField.setColumns(10);
  307. critratingField.addPropertyChangeListener("value", this);
  308.  
  309. hasteratingField = new JFormattedTextField(NumberFormat.getNumberInstance());
  310. hasteratingField.setValue(hasterating);
  311. hasteratingField.setColumns(10);
  312. hasteratingField.addPropertyChangeListener("value", this);
  313.  
  314. stacksField = new JFormattedTextField(NumberFormat.getNumberInstance());
  315. stacksField.setValue(stacks);
  316. stacksField.setColumns(10);
  317. stacksField.addPropertyChangeListener("value", this);
  318.  
  319. intellectLabel.setLabelFor(intellectField);
  320. spellpowerLabel.setLabelFor(spellpowerField);
  321. critratingLabel.setLabelFor(critratingField);
  322. hasteratingLabel.setLabelFor(hasteratingField);
  323. stacksLabel.setLabelFor(stacksField);
  324.  
  325. //create new checkboxes
  326. KingsBox = new JCheckBox("Kings", Kings);
  327. ForKingsBox = new JCheckBox("Blessing of Forgotten Kings", ForKings);
  328. WrathOfAirBox = new JCheckBox("Wrath Of Air", WrathOfAir);
  329. MoonkinAuraBox = new JCheckBox("Moonkin Aura", MoonkinAura);
  330. BerserkingBox = new JCheckBox("Berserking", Berserking);
  331. BloodlustBox = new JCheckBox("Bloodlust", Bloodlust);
  332. TreeOfLifeBox = new JCheckBox("Tree Of Life/Improved Devotion Aura", TreeOfLife);
  333. ICCBuffBox = new JCheckBox("ICC Buff (25%)", ICCBuff);
  334. MOTWBox = new JCheckBox("Mark Of The Wild", MOTW);
  335.  
  336. KingsBox.addItemListener(this);
  337. WrathOfAirBox.addItemListener(this);
  338. MoonkinAuraBox.addItemListener(this);
  339. BerserkingBox.addItemListener(this);
  340. BloodlustBox.addItemListener(this);
  341. TreeOfLifeBox.addItemListener(this);
  342. ICCBuffBox.addItemListener(this);
  343.  
  344. JPanel labelPane = new JPanel(new GridLayout(0,1));
  345. labelPane.add(intellectLabel);
  346. labelPane.add(spellpowerLabel);
  347. labelPane.add(critratingLabel);
  348. labelPane.add(hasteratingLabel);
  349. labelPane.add(stacksLabel);
  350.  
  351. JPanel fieldPane = new JPanel(new GridLayout(0,1));
  352. fieldPane.add(intellectField);
  353. fieldPane.add(spellpowerField);
  354. fieldPane.add(critratingField);
  355. fieldPane.add(hasteratingField);
  356. fieldPane.add(stacksField);
  357.  
  358. JPanel statsPanel = new JPanel(new GridBagLayout());
  359. textArea = new JTextArea(3,50);
  360. textArea.setEditable(false);
  361.  
  362. JPanel buffPanel = new JPanel(new GridLayout(0,1));
  363. buffPanel.add(KingsBox);
  364. buffPanel.add(ForKingsBox);
  365. buffPanel.add(MOTWBox);
  366. buffPanel.add(WrathOfAirBox);
  367. buffPanel.add(MoonkinAuraBox);
  368. buffPanel.add(TreeOfLifeBox);
  369. buffPanel.add(ICCBuffBox);
  370. buffPanel.add(BerserkingBox);
  371. buffPanel.add(BloodlustBox);
  372.  
  373.  
  374.  
  375. Border blackline = BorderFactory.createLineBorder(Color.black);
  376.  
  377. labelPane.setBorder(blackline);
  378.  
  379. GridBagConstraints c = new GridBagConstraints();
  380. c.fill = GridBagConstraints.BOTH;
  381. c.gridx = 0;
  382. c.gridy = 0;
  383. c.weightx = 1;
  384. c.weighty = 1;
  385. //c.insets = new Insets(0,0,300,0);
  386. //c.anchor = GridBagConstraints.FIRST_LINE_END;
  387. statsPanel.add(labelPane, c);
  388.  
  389. c = new GridBagConstraints();
  390. c.fill = GridBagConstraints.VERTICAL;
  391. c.gridx = 1;
  392. c.gridy = 0;
  393. c.weightx = 1;
  394. c.weighty = 1;
  395. //c.insets = new Insets(0,0,300,0);
  396. c.anchor = GridBagConstraints.FIRST_LINE_START;
  397. statsPanel.add(fieldPane, c);
  398.  
  399. c = new GridBagConstraints();
  400. c.fill = GridBagConstraints.VERTICAL;
  401. c.gridx = 2;
  402. c.gridy = 0;
  403. c.weightx = 1;
  404. c.weighty = 1;
  405. //c.insets = new Insets(0,0,300,0);
  406. c.anchor = GridBagConstraints.FIRST_LINE_START;
  407. statsPanel.add(buffPanel, c);
  408.  
  409. c = new GridBagConstraints();
  410. c.gridx = 0;
  411. c.gridy = 1;
  412. c.gridwidth = 2;
  413. c.weightx = 1;
  414. c.weighty = 3;
  415. c.anchor = GridBagConstraints.FIRST_LINE_START;
  416. statsPanel.add(textArea, c);
  417.  
  418. return statsPanel;
  419. }
  420.  
  421. public void propertyChange(PropertyChangeEvent e)
  422. {
  423. Object source = e.getSource();
  424. if (source == intellectField)
  425. intellect = ((Number)intellectField.getValue()).doubleValue();
  426. else if (source == spellpowerField)
  427. spellpower = ((Number)spellpowerField.getValue()).doubleValue();
  428. else if (source == critratingField)
  429. critrating = ((Number)critratingField.getValue()).doubleValue();
  430. else if (source == hasteratingField)
  431. hasterating = ((Number)hasteratingField.getValue()).doubleValue();
  432. else if (source == stacksField)
  433. stacks = ((Number)stacksField.getValue()).doubleValue();
  434.  
  435. simulate();
  436. }
  437.  
  438. public void itemStateChanged (ItemEvent e)
  439. {
  440. Object source = e.getItemSelectable();
  441.  
  442. if (source == KingsBox)
  443. Kings = !Kings;
  444. else if (source == ForKingsBox)
  445. ForKings = !ForKings;
  446. else if (source == WrathOfAirBox)
  447. WrathOfAir = !WrathOfAir;
  448. else if (source == MoonkinAuraBox)
  449. MoonkinAura = !MoonkinAura;
  450. else if (source == BloodlustBox)
  451. Bloodlust = !Bloodlust;
  452. else if (source == BerserkingBox)
  453. Berserking = !Berserking;
  454. else if (source == TreeOfLifeBox)
  455. TreeOfLife = !TreeOfLife;
  456. else if (source == ICCBuffBox)
  457. ICCBuff = !ICCBuff;
  458. else if (source == MOTWBox)
  459. MOTW = !MOTW;
  460.  
  461.  
  462. simulate();
  463. }
  464. }