/******************************************************************************* * Copyright (c) 2014, 2015 Scott Clarke (scott@dawg6.com). * * This file is part of Dawg6's Demon Hunter DPS Calculator. * * Dawg6's Demon Hunter DPS Calculator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Dawg6's Demon Hunter DPS Calculator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package com.dawg6.web.dhcalc.client; import java.util.List; import java.util.Vector; import com.dawg6.gwt.client.widgets.ImageButton; import com.dawg6.gwt.client.widgets.SimpleCaptionPanel; import com.dawg6.web.dhcalc.shared.calculator.Util; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HasHorizontalAlignment; import com.google.gwt.user.client.ui.HasVerticalAlignment; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.SimpleCheckBox; import com.google.gwt.user.client.ui.Widget; public class ParagonPanel extends Composite { private final NumberSpinner paragonIAS; private final NumberSpinner paragonCC; private final NumberSpinner paragonCHD; private final Label chdLabel; private final Label ccLabel; private final Label iasLabel; private final NumberSpinner paragonCDR; private final Label cdrLabel; private final Label lblNewLabel; private final Label lblTotal; private final Label total; private final Label lblRequiredParagonLevel; private final Label level; private final HorizontalPanel horizontalPanel; private final Label label_3; private final Label lblParagonHatredpoints; private final Label lblParagonRcrpoints; private final Label rcrLabel; private final Label hatredLabel; private final NumberSpinner paragonHatred; private final NumberSpinner paragonRCR; private final Label lblParagonDexteritypoints; private final NumberSpinner paragonDexterity; private final Label dexLabel; private final Label lblOffensePoints; private final Label label_4; private final NumberSpinner paragonAD; private final Label adLabel; private final NumberSpinner paragonLevel; private int firstRow = 999; private int lastRow = -1; private final int[] priorities = { 0, 1, 2, 3}; private final FlexTable offenseTable; private final List<Listener> listeners = new Vector<Listener>(); private boolean disableListeners = false; private final SimpleCheckBox autoFill; public interface Listener { void paragonsChanged(); } public ParagonPanel() { SimpleCaptionPanel caption = new SimpleCaptionPanel("Paragon"); initWidget(caption); FlexTable table1 = new FlexTable(); caption.setContentWidget(table1); FlexTable table2 = new FlexTable(); table2.setCellPadding(2); table1.setWidget(0, 0, table2); table1.getFlexCellFormatter().setVerticalAlignment(0, 0, HasVerticalAlignment.ALIGN_TOP); offenseTable = new FlexTable(); offenseTable.setCellPadding(2); table1.setWidget(0, 1, offenseTable); table1.getFlexCellFormatter().setVerticalAlignment(0, 1, HasVerticalAlignment.ALIGN_TOP); int row = 0; lblNewLabel = new Label( "Note: Paragon points other than Dexterity must be adjusted manually after import" + " (they are not available via the battle.net API)." + " Offense points will be filled in order of priority (top to bottom)." ); lblNewLabel.setWordWrap(true); lblNewLabel.addStyleName("boldText"); table2.setWidget(row, 0, lblNewLabel); lblNewLabel.setWidth("300px"); table2.getFlexCellFormatter().setColSpan(row, 0, 3); row++; lblParagonDexteritypoints = new Label("Paragon Dexterity (points):"); lblParagonDexteritypoints.setWordWrap(false); table2.setWidget(row, 0, lblParagonDexteritypoints); paragonDexterity = new NumberSpinner(); paragonDexterity.setVisibleLength(6); paragonDexterity.setTitle("Each paragon Dexterity point adds 5 Dexterity"); table2.setWidget(row, 1, paragonDexterity); paragonDexterity.setMax(10000); paragonDexterity.addChangeHandler(new ChangeHandler(){ @Override public void onChange(ChangeEvent event) { updateDexterityLabel(); valuesChanged(); }}); dexLabel = new Label("(0)"); dexLabel.setStyleName("boldText"); table2.setWidget(row, 2, dexLabel); row++; lblParagonHatredpoints = new Label("Paragon Hatred (points):"); lblParagonHatredpoints.setWordWrap(false); table2.setWidget(row, 0, lblParagonHatredpoints); paragonHatred = new NumberSpinner(); paragonHatred.setVisibleLength(6); paragonHatred.setTitle("Each paragon Hatred point adds 0.5 Max Hatred"); paragonHatred.setText("0"); table2.setWidget(row, 1, paragonHatred); paragonHatred.setMax(50); paragonHatred.addChangeHandler(new ChangeHandler(){ @Override public void onChange(ChangeEvent event) { updateHatredLabel(); valuesChanged(); }}); hatredLabel = new Label("(0)"); hatredLabel.setStyleName("boldText"); table2.setWidget(row, 2, hatredLabel); row++; lblParagonRcrpoints = new Label("Paragon RCR (points):"); lblParagonRcrpoints.setWordWrap(false); table2.setWidget(row, 0, lblParagonRcrpoints); paragonRCR = new NumberSpinner(); paragonRCR.setVisibleLength(6); paragonRCR.setTitle("Each paragon RCR point adds 0.1% Resource Cost Reduction"); paragonRCR.setText("0"); table2.setWidget(row, 1, paragonRCR); paragonRCR.setMax(50); paragonRCR.addChangeHandler(new ChangeHandler(){ @Override public void onChange(ChangeEvent event) { updateRCRLabel(); valuesChanged(); }}); rcrLabel = new Label("(0%)"); rcrLabel.setStyleName("boldText"); table2.setWidget(row, 2, rcrLabel); row++; label_4 = new Label("Paragon Area Damage (points):"); label_4.setWordWrap(false); table2.setWidget(row, 0, label_4); paragonAD = new NumberSpinner(); paragonAD.setVisibleLength(6); paragonAD.setTitle("Each paragon AD point adds 1% Area Damage"); paragonAD.setText("0"); table2.setWidget(row, 1, paragonAD); paragonAD.addChangeHandler(new ChangeHandler(){ @Override public void onChange(ChangeEvent event) { updateADLabel(); valuesChanged(); }}); adLabel = new Label("(0%)"); adLabel.setStyleName("boldText"); table2.setWidget(row, 2, adLabel); row = 0; Label label1 = new Label("Paragon Level:"); label1.setWordWrap(false); offenseTable.setWidget(row, 0, label1); paragonLevel = new NumberSpinner(); paragonLevel.setTitle("Hero Paragon Level"); paragonLevel.setMin(0); paragonLevel.setVisibleLength(6); offenseTable.setWidget(row, 1, paragonLevel); paragonLevel.addChangeHandler(new ChangeHandler(){ @Override public void onChange(ChangeEvent event) { valuesChanged(); }}); Button calcButton = new Button("Fill"); calcButton.setTitle("Click to fill paragon offense points based on level and order of priorities"); calcButton.setWidth("100%"); offenseTable.setWidget(row, 3, calcButton); offenseTable.getFlexCellFormatter().setColSpan(row, 3, 2); calcButton.addClickHandler(new ClickHandler(){ @Override public void onClick(ClickEvent event) { fillOffensePoints(); }}); row++; Label label2 = new Label("Auto Fill on Import:"); label2.setWordWrap(false); offenseTable.setWidget(row, 0, label2); autoFill = new SimpleCheckBox(); autoFill.setTitle("Check this to fill Offense paragon points on import"); offenseTable.setWidget(row, 1, autoFill); row++; lblOffensePoints = new Label("Offense Points:"); lblOffensePoints.setWordWrap(false); lblOffensePoints.addStyleName("boldText"); offenseTable.setWidget(row, 0, lblOffensePoints); offenseTable.getFlexCellFormatter().setColSpan(row, 0, 3); row++; Label label = new Label("Paragon IAS (points):"); label.setWordWrap(false); offenseTable.setWidget(row, 0, label); paragonIAS = new NumberSpinner(); paragonIAS.setTitle("Each paragon IAS point adds .2% IAS"); paragonIAS.setVisibleLength(6); offenseTable.setWidget(row, 1, paragonIAS); paragonIAS.addChangeHandler(new ChangeHandler(){ @Override public void onChange(ChangeEvent event) { updateIASLabel(); updateTotal(); }}); iasLabel = new Label("(0%)"); iasLabel.setStyleName("boldText"); offenseTable.setWidget(row, 2, iasLabel); addUpDownButtons(offenseTable, row); row++; Label paragonCDRlabel = new Label("Paragon CDR (points):"); paragonCDRlabel.setWordWrap(false); offenseTable.setWidget(row, 0, paragonCDRlabel); paragonCDR = new NumberSpinner(); paragonCDR.setTitle("Each paragon CDR point adds .2% Cooldown Reduction"); paragonCDR.setVisibleLength(6); offenseTable.setWidget(row, 1, paragonCDR); paragonCDR.addChangeHandler(new ChangeHandler(){ @Override public void onChange(ChangeEvent event) { updateCDRLabel(); updateTotal(); }}); cdrLabel = new Label("(0%)"); cdrLabel.setStyleName("boldText"); offenseTable.setWidget(row, 2, cdrLabel); addUpDownButtons(offenseTable, row); row++; Label label_1 = new Label("Paragon CHC (points):"); label_1.setWordWrap(false); offenseTable.setWidget(row, 0, label_1); paragonCC = new NumberSpinner(); paragonCC.setTitle("Each paragon CHC point adds .1 % Crit Chance"); paragonCC.setVisibleLength(6); paragonCC.setText("0"); offenseTable.setWidget(row, 1, paragonCC); paragonCC.addChangeHandler(new ChangeHandler(){ @Override public void onChange(ChangeEvent event) { updateCCLabel(); updateTotal(); }}); ccLabel = new Label("(0%)"); ccLabel.setStyleName("boldText"); offenseTable.setWidget(row, 2, ccLabel); addUpDownButtons(offenseTable, row); row++; Label label_2 = new Label("Paragon CHD (points):"); label_2.setWordWrap(false); offenseTable.setWidget(row, 0, label_2); paragonCHD = new NumberSpinner(); paragonCHD.setTitle("Each paragon CHD point add 1% Crit Hit Damage"); paragonCHD.setVisibleLength(6); paragonCHD.setText("0"); offenseTable.setWidget(row, 1, paragonCHD); paragonCHD.addChangeHandler(new ChangeHandler(){ @Override public void onChange(ChangeEvent event) { updateCHDLabel(); updateTotal(); }}); chdLabel = new Label("(0%)"); chdLabel.setStyleName("boldText"); offenseTable.setWidget(row, 2, chdLabel); addUpDownButtons(offenseTable, row); row++; horizontalPanel = new HorizontalPanel(); horizontalPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE); horizontalPanel.setSpacing(2); offenseTable.setWidget(row, 0, horizontalPanel); lblTotal = new Label("Total Offense Points:"); horizontalPanel.add(lblTotal); lblTotal.setWordWrap(false); total = new Label("0"); horizontalPanel.add(total); total.setStyleName("boldText"); lblRequiredParagonLevel = new Label(" (Requires paragon level"); horizontalPanel.add(lblRequiredParagonLevel); lblRequiredParagonLevel.setWordWrap(false); level = new Label("0"); horizontalPanel.add(level); level.setStyleName("boldText"); label_3 = new Label(")"); label_3.setWordWrap(false); horizontalPanel.add(label_3); offenseTable.getCellFormatter().setHorizontalAlignment(row, 0, HasHorizontalAlignment.ALIGN_RIGHT); offenseTable.getFlexCellFormatter().setColSpan(row, 0, 5); paragonIAS.setMax(50); paragonCC.setMax(50); paragonCHD.setMax(50); paragonCDR.setMax(50); paragonAD.setMax(50); } private void addUpDownButtons(final FlexTable table, final int row) { if (row < firstRow) firstRow = row; if (row > lastRow) lastRow = row; ImageButton up = new ImageButton(Images.UP, "move up in priority"); ImageButton down = new ImageButton(Images.DOWN, "move down in priority"); table.setWidget(row, 3, up); table.setWidget(row, 4, down); up.addClickHandler(new ClickHandler(){ @Override public void onClick(ClickEvent event) { if (row > firstRow) moveFields(table, row, -1); }}); down.addClickHandler(new ClickHandler(){ @Override public void onClick(ClickEvent event) { if (row < lastRow) moveFields(table, row, 1); }}); } protected void moveFields(FlexTable table, int row, int dir) { for (int i = 0; i < 3; i++) { Widget a = table.getWidget(row, i); Widget b = table.getWidget(row + dir, i); table.setWidget(row, i, b); table.setWidget(row + dir, i, a); } int a = priorities[row - firstRow]; int b = priorities[row - firstRow + dir]; priorities[row - firstRow] = b; priorities[row - firstRow + dir] = a; } public void fillOffensePoints() { calculateParagonPoints(this.offenseTable); } protected void calculateParagonPoints(FlexTable table) { int level = this.paragonLevel.getValue(); int points = (level + 2) / 4; for (int i = 0; i < 4; i++) { NumberSpinner n = (NumberSpinner)table.getWidget(firstRow + i, 1); int value = Math.min(50, points); n.setValue(value); points -= value; } } protected void updateTotal() { int total = paragonIAS.getValue() + paragonCDR.getValue() + paragonCC.getValue() + paragonCHD.getValue(); this.total.setText(String.valueOf(total)); this.level.setText(String.valueOf(Math.max(0, total * 4 - 2))); valuesChanged(); } protected void updateCDRLabel() { cdrLabel.setText("(" + Util.format((double)paragonCDR.getValue() * .2) + "%)"); } protected void updateCHDLabel() { chdLabel.setText("(" + paragonCHD.getValue() + "%)"); } protected void updateCCLabel() { ccLabel.setText("(" + Util.format((double)paragonCC.getValue() / 10.0) + "%)"); } protected void updateIASLabel() { iasLabel.setText("(" + Util.format((double)paragonIAS.getValue() * .2) + "%)"); } protected void updateRCRLabel() { rcrLabel.setText("(" + Util.format((double)paragonRCR.getValue() * .2) + "%)"); } protected void updateADLabel() { adLabel.setText("(" + Util.format(paragonAD.getValue()) + "%)"); } protected void updateHatredLabel() { hatredLabel.setText("(" + Util.format((double)paragonRCR.getValue() * .5) + ")"); } protected void updateDexterityLabel() { dexLabel.setText("(" + Util.format(paragonDexterity.getValue() * 5) + ")"); } public NumberSpinner getParagonIAS() { return paragonIAS; } public NumberSpinner getParagonCC() { return paragonCC; } public NumberSpinner getParagonCHD() { return paragonCHD; } public NumberSpinner getParagonCDR() { return paragonCDR; } public NumberSpinner getParagonHatred() { return paragonHatred; } public NumberSpinner getParagonRCR() { return paragonRCR; } public NumberSpinner getParagonLevel() { return paragonLevel; } public NumberSpinner getParagonAD() { return paragonAD; } public NumberSpinner getParagonDexterity() { return paragonDexterity; } public String getParagonPriorities() { StringBuffer buf = new StringBuffer(); for (int i = 0; i < 4; i++) buf.append(String.valueOf(this.priorities[i])); return buf.toString(); } public void setParagonPriorities(String s) { if (s == null) s = "0123"; s = s.trim(); if (s.length() != 4) s = "0123"; int[] def = { 0, 1, 2, 3}; int[] p = new int[4]; for (int i = 0; i < 4; i++) { char c = s.charAt(i); if ((c < '0') || (c > '3')) { p = def; break; } else { p[i] = c - '0'; } } setParagonPriorities(p); } public void setParagonPriorities(int[] p) { Widget[][] widgets = new Widget[4][3]; for (int i = 0; i < 4; i++) { for (int j = 0; j < 3; j++) { widgets[priorities[i]][j] = offenseTable.getWidget(firstRow + i, j); } } for (int i = 0; i < 4; i++) { for (int j = 0; j < 3; j++) { Widget w = widgets[p[i]][j]; offenseTable.setWidget(firstRow + i, j, w); } priorities[i] = p[i]; } } public void enableListeners() { this.disableListeners = false; } public void disableListeners() { this.disableListeners = false; } private void valuesChanged() { if (!disableListeners) { for (Listener l : listeners) l.paragonsChanged(); } } public void addListener(Listener l) { if (!listeners.contains(l)) listeners.add(l); } public void removeListener(Listener l) { listeners.remove(l); } public SimpleCheckBox getAutoFill() { return autoFill; } }