//Version 5
import javax.
swing.
border.
Border;
//import java.awt.Insets;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
{
public static double intellect = 1489; //base intellect
public static double spellpower = 3301;
public static double critrating = 689;
public static double hasterating = 1269;
public static double stacks = 50;
public static final double classcrit = 5 + 4 + 2.2; //percent chance to get crits from class and global crit talents (
static boolean Kings = true; //10% to all stats
static boolean WrathOfAir = true; //5% spell haste
static boolean MoonkinAura = true; //3% haste, 5% crit
static boolean Berserking = false; //Troll racial, 20% haste
static boolean Bloodlust = false; //30% haste, but does not affect GCD
static boolean TreeOfLife = true; //6% additional healing done
static boolean ICCBuff = true; //25% additional healing done
static boolean MOTW = true; //Increases all atributes by 37
static boolean ForKings = false; //8% to all stats
static XYSeries RHHCHHseries;
static XYSeries RHHHHseries;
static XYSeries RHHHLseries;
static XYSeries HHHHHseries;
//Called when this applet is loaded into the browser
public void init()
{
XYDataset dataset = createDataset();
JFreeChart hpsChart = createChart(dataset);
ChartPanel chartPanel = new ChartPanel(hpsChart);
JPanel statsPanel = createStatsPanel
();
tabs.addTab("Graph", chartPanel);
tabs.addTab("Stats", statsPanel);
tabs.
setPreferredSize(new Dimension(1920,
1200));
setContentPane(tabs);
simulate();
}
public void simulate()
{
/* base healing amounts (non-crit) before non-character buffs */
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
double HWbase = 1.25 * 1.1 * (3250 + spellpower * 1.8114);
double CHbase = 1.2 * 1.1 * (1130 + spellpower * 1.3428);
double RTbase = 1.1 * (1670 + spellpower * 0.402);
double RThot = 1.1 * (334 + spellpower * 0.188);
double ELhot = 1.1 * (652 + spellpower * 12/15 * 1.88 * 5/11) / 4;
double critchance = (critrating / 45.91 + classcrit + intellect / 166.6667) / 100;
double hastemultiplier = 1 / (1 + hasterating / 3279);
double AAcoeff = 0.297;
double GCD = 1.5;
/* various buffs (comment out the line if you don't want those buffs calculated; bloodlust is located after GCD calculation) */
if (MOTW) { intellect += 37; } //MOTW
if (Kings) { critchance += 0.1 * intellect / 166.6667 / 100; } //Kings
if (ForKings) { critchance += 0.08 * intellect / 166.6667 / 100; } //Blessing of Forgotten Kings
if (WrathOfAir) { hastemultiplier *= 1 / (1 + 5.0/100); } //WoA
if (MoonkinAura) { hastemultiplier *= 1 / (1 + 3.0/100); critchance += 0.05; } //Moonkin aura
if (Berserking) { hastemultiplier *= 1 / (1 + 20.0/100); } //Berserking
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
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
GCD = GCD * hastemultiplier >= 1 ? GCD * hastemultiplier : 1.0;
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
textArea.setText("LHW base heal (w/ ES on the target):\t" + LHWbase);
textArea.append("\nHW base heal:\t\t\t" + HWbase);
textArea.append("\nCH base heal:\t\t\t" + CHbase);
textArea.append("\nRT base heal:\t\t\t" + RTbase);
textArea.append("\nRT tick:\t\t\t" + RThot);
textArea.append("\nEL tick:\t\t\t" + ELhot);
textArea.append("\n\nCrit chance:\t\t\t" + critchance * 100 + "%");
textArea.append("\nHaste multiplier:\t\t" + hastemultiplier);
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
double LHWtime = 1.5 * hastemultiplier >= GCD ? 1.5 * hastemultiplier : GCD;
double CHtime = 2.5 * hastemultiplier >= GCD ? 2.5 * hastemultiplier : GCD;
double HWtime = 2.5 * hastemultiplier >= GCD ? 2.5 * hastemultiplier : GCD;
double HWTWtime = 2.5 * .7 * hastemultiplier >= 1 ? 2.5 * .7 * hastemultiplier : 1;
double HWTW2pctime = HWTWtime * 1 / (1 + 20.0/100) >= 1 ? HWTWtime * 1 / (1 + 20.0/100) : 1;
System.
out.
println("RT cast time:\t\t\t" + RTtime
);
System.
out.
println("LHW cast time:\t\t\t" + LHWtime
);
System.
out.
println("CH time:\t\t\t" + CHtime
);
System.
out.
println("HW cast time:\t\t\t" + HWtime
);
System.
out.
println("HW w/ Tidal Waves cast time:\t" + HWTWtime
);
System.
out.
println("HW w/ Tidal Waves + 2pct10:\t" + HWTW2pctime
);
double RHHCHHtime = RTtime + //RT
HWTW2pctime + //the HW after the RT due to 2pcT10
HWTWtime * 3 + //the other 3 HW
CHtime; //CH
if (RHHCHHtime < 6) RHHCHHtime = 6;
double RHHHHtime = RTtime +
HWTW2pctime +
HWTWtime + //the next HW
HWtime * 2; //the next 2 HW
if (RHHHHtime < 6) RHHHHtime = 6;
double RHHHLtime = RTtime +
HWTW2pctime +
HWTWtime + //the next HW
HWtime + //the next HW
LHWtime;
if (RHHHLtime < 6) RHHHLtime = 6;
double RHHHHHtime = RTtime +
HWTW2pctime +
HWTWtime + //the next HW
HWtime * 3; //the next 3 HW
if (RHHHHHtime < 6) RHHHHHtime = 6;
double HHHHHtime = HWtime * 5; //5 HW
textArea.append("\n\nRHHCHH rotation time:\t\t" + RHHCHHtime);
textArea.append("\nRHHHH rotation time:\t\t" + RHHHHtime);
//System.out.println("RHHHHH rotation time:\t\t" + RHHHHHtime);
textArea.append("\nRHHHL rotation time:\t\t" + RHHHLtime);
textArea.append("\nHHHHH rotation time:\t\t" + HHHHHtime);
//System.out.println();
long crossoverStacks = 0;
RHHCHHseries.clear();
RHHHHseries.clear();
RHHHLseries.clear();
HHHHHseries.clear();
double RHHCHHavg = 0;
double RHHHHavg = 0;
double RHHHLavg = 0;
double RHHHHHavg = 0;
double HHHHHavg = 0;
for (int i = 0; i <= stacks; i++)
{
double stacksMultiplier = 1 + 0.1 * i;
double HWavg = stacksMultiplier * (HWbase * (1 - critchance) + HWbase * critchance * 1.5 + HWbase * critchance * 1.5 * AAcoeff * stacksMultiplier);
double LHWavg = stacksMultiplier * (LHWbase * (1 - critchance) + LHWbase * critchance * 1.5 + LHWbase * critchance * 1.5 * AAcoeff * stacksMultiplier);
double CHavg = stacksMultiplier * (CHbase * (1 - critchance) + CHbase * critchance * 1.5 + CHbase * 1.5 * critchance * .25); //4 pc T10 Rsham
double RTavg = stacksMultiplier * (RTbase * (1 - critchance) + RTbase * critchance * 1.5 + RTbase * critchance * 1.5 * AAcoeff);
double RTtickavg = stacksMultiplier * RThot;
double ELhotavg = stacksMultiplier * ELhot;
RHHCHHavg = RTavg + RTtickavg *
Math.
floor((RTtime + HWTW2pctime + HWTWtime
) /
3) + HWavg *
4 +
1.25 * CHavg;
RHHHHavg = RTavg + RTtickavg *
Math.
floor((RHHHHtime
) /
3) + HWavg *
2 + HWavg *
2;
RHHHLavg = RTavg + RTtickavg *
Math.
floor((RHHHLtime
) /
3) + HWavg *
2 + HWavg + LHWavg;
RHHHHHavg = RTavg + RTtickavg *
Math.
floor((RHHHHHtime
) /
3) + HWavg *
2 + HWavg *
3;
HHHHHavg = HWavg * 5;
RHHCHHseries.add(i, RHHCHHavg/RHHCHHtime);
RHHHHseries.add(i, RHHHHavg/RHHHHtime);
RHHHLseries.add(i, RHHHLavg/RHHHLtime);
HHHHHseries.add(i, HHHHHavg/HHHHHtime);
/*System.out.println();
System.out.println("Stacks:\t" + i);
System.out.println("RHHCHH HPS:\t" + RHHCHHavg/RHHCHHtime);
System.out.println("RHHHH HPS:\t" + RHHHHavg/RHHHHtime);*/
//System.out.println("RHHHL HPS:\t" + RHHHLavg/RHHHLtime);
//System.out.println("RHHHHH HPS:\t" + RHHHHHavg/RHHHHHtime);
//System.out.println("HHHHH HPS:\t" + HHHHHavg/HHHHHtime);
//System.out.println("% difference:\t" + (RHHHHavg/RHHHHtime - RHHCHHavg/RHHCHHtime) / (RHHCHHavg/RHHCHHtime) * 100 + "%");
if (RHHHHavg/RHHHHtime > RHHCHHavg/RHHCHHtime && crossoverStacks == 0)
crossoverStacks = i;
}
if (RHHHHavg/RHHHHtime > RHHCHHavg/RHHCHHtime)
textArea.append("\n\nRHHHH overtakes RHHCHH at " + crossoverStacks + " stacks");
else
textArea.append("\n\nRHHHH never overtakes RHHCHH, please extend simulated stacks");
}
public XYDataset createDataset()
{
RHHCHHseries = new XYSeries("RHHCHH");
RHHHHseries = new XYSeries("RHHHH");
RHHHLseries = new XYSeries("RHHHL");
HHHHHseries = new XYSeries("Healing Wave Spam");
XYSeriesCollection dataset = new XYSeriesCollection();
dataset.addSeries(RHHCHHseries);
dataset.addSeries(RHHHHseries);
dataset.addSeries(RHHHLseries);
dataset.addSeries(HHHHHseries);
return dataset;
}
public JFreeChart createChart(XYDataset dataset)
{
JFreeChart chart = ChartFactory.createXYLineChart(
"HPS vs Stacks", // chart title
"Stacks", // x axis label
"HPS", // y axis label
dataset, // data
PlotOrientation.VERTICAL,
true, // include legend
true, // tooltips
false // urls
);
// get a reference to the plot for further customisation...
XYPlot plot = (XYPlot) chart.getPlot();
XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer();
renderer.setBaseShapesVisible(false);
renderer.setBaseShapesFilled(false);
// change the auto tick unit selection to integer units only...
NumberAxis domainAxis = (NumberAxis) plot.getDomainAxis();
NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
domainAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
return chart;
}
public JPanel createStatsPanel
() {
//create new labels
JLabel stacksLabel =
new JLabel("Number of stacks to simulate");
//create new fields
intellectField.setValue(intellect);
intellectField.setColumns(10);
intellectField.addPropertyChangeListener("value", this);
spellpowerField.setValue(spellpower);
spellpowerField.setColumns(10);
spellpowerField.addPropertyChangeListener("value", this);
critratingField.setValue(critrating);
critratingField.setColumns(10);
critratingField.addPropertyChangeListener("value", this);
hasteratingField.setValue(hasterating);
hasteratingField.setColumns(10);
hasteratingField.addPropertyChangeListener("value", this);
stacksField.setValue(stacks);
stacksField.setColumns(10);
stacksField.addPropertyChangeListener("value", this);
intellectLabel.setLabelFor(intellectField);
spellpowerLabel.setLabelFor(spellpowerField);
critratingLabel.setLabelFor(critratingField);
hasteratingLabel.setLabelFor(hasteratingField);
stacksLabel.setLabelFor(stacksField);
//create new checkboxes
ForKingsBox =
new JCheckBox("Blessing of Forgotten Kings", ForKings
);
WrathOfAirBox =
new JCheckBox("Wrath Of Air", WrathOfAir
);
MoonkinAuraBox =
new JCheckBox("Moonkin Aura", MoonkinAura
);
BerserkingBox =
new JCheckBox("Berserking", Berserking
);
BloodlustBox =
new JCheckBox("Bloodlust", Bloodlust
);
TreeOfLifeBox =
new JCheckBox("Tree Of Life/Improved Devotion Aura", TreeOfLife
);
ICCBuffBox =
new JCheckBox("ICC Buff (25%)", ICCBuff
);
MOTWBox =
new JCheckBox("Mark Of The Wild", MOTW
);
KingsBox.addItemListener(this);
WrathOfAirBox.addItemListener(this);
MoonkinAuraBox.addItemListener(this);
BerserkingBox.addItemListener(this);
BloodlustBox.addItemListener(this);
TreeOfLifeBox.addItemListener(this);
ICCBuffBox.addItemListener(this);
labelPane.add(intellectLabel);
labelPane.add(spellpowerLabel);
labelPane.add(critratingLabel);
labelPane.add(hasteratingLabel);
labelPane.add(stacksLabel);
fieldPane.add(intellectField);
fieldPane.add(spellpowerField);
fieldPane.add(critratingField);
fieldPane.add(hasteratingField);
fieldPane.add(stacksField);
textArea.setEditable(false);
buffPanel.add(KingsBox);
buffPanel.add(ForKingsBox);
buffPanel.add(MOTWBox);
buffPanel.add(WrathOfAirBox);
buffPanel.add(MoonkinAuraBox);
buffPanel.add(TreeOfLifeBox);
buffPanel.add(ICCBuffBox);
buffPanel.add(BerserkingBox);
buffPanel.add(BloodlustBox);
labelPane.setBorder(blackline);
c.gridx = 0;
c.gridy = 0;
c.weightx = 1;
c.weighty = 1;
//c.insets = new Insets(0,0,300,0);
//c.anchor = GridBagConstraints.FIRST_LINE_END;
statsPanel.add(labelPane, c);
c.gridx = 1;
c.gridy = 0;
c.weightx = 1;
c.weighty = 1;
//c.insets = new Insets(0,0,300,0);
statsPanel.add(fieldPane, c);
c.gridx = 2;
c.gridy = 0;
c.weightx = 1;
c.weighty = 1;
//c.insets = new Insets(0,0,300,0);
statsPanel.add(buffPanel, c);
c.gridx = 0;
c.gridy = 1;
c.gridwidth = 2;
c.weightx = 1;
c.weighty = 3;
statsPanel.add(textArea, c);
return statsPanel;
}
{
Object source = e.
getSource();
if (source == intellectField)
intellect =
((Number)intellectField.
getValue()).
doubleValue();
else if (source == spellpowerField)
spellpower =
((Number)spellpowerField.
getValue()).
doubleValue();
else if (source == critratingField)
critrating =
((Number)critratingField.
getValue()).
doubleValue();
else if (source == hasteratingField)
hasterating =
((Number)hasteratingField.
getValue()).
doubleValue();
else if (source == stacksField)
stacks =
((Number)stacksField.
getValue()).
doubleValue();
simulate();
}
{
Object source = e.
getItemSelectable();
if (source == KingsBox)
Kings = !Kings;
else if (source == ForKingsBox)
ForKings = !ForKings;
else if (source == WrathOfAirBox)
WrathOfAir = !WrathOfAir;
else if (source == MoonkinAuraBox)
MoonkinAura = !MoonkinAura;
else if (source == BloodlustBox)
Bloodlust = !Bloodlust;
else if (source == BerserkingBox)
Berserking = !Berserking;
else if (source == TreeOfLifeBox)
TreeOfLife = !TreeOfLife;
else if (source == ICCBuffBox)
ICCBuff = !ICCBuff;
else if (source == MOTWBox)
MOTW = !MOTW;
simulate();
}
}