/******************************************************************************* * 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.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.Vector; import com.dawg6.gwt.client.widgets.SimpleCaptionPanel; import com.dawg6.web.dhcalc.shared.calculator.AttributeData; import com.dawg6.web.dhcalc.shared.calculator.ItemHolder; import com.dawg6.web.dhcalc.shared.calculator.ItemSet; import com.dawg6.web.dhcalc.shared.calculator.Slot; import com.dawg6.web.dhcalc.shared.calculator.SpecialItemType; import com.dawg6.web.dhcalc.shared.calculator.SpecialItemType.Attribute; 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.Anchor; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HasVerticalAlignment; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.ListBox; import com.google.gwt.user.client.ui.SimpleCheckBox; public class ItemPanel extends Composite { private final NumberSpinner areaDamageEquipment; private final NumberSpinner eliteDamagePercent; private final FlexTable table1; private final Map<Slot, Anchor> anchors = new TreeMap<Slot, Anchor>(); private final Map<Slot, ListBox> listBoxes = new TreeMap<Slot, ListBox>(); private boolean disableListeners; private final List<ChangeHandler> handlers = new Vector<ChangeHandler>(); private final Map <ItemSet, NumberSpinner> setCounts = new TreeMap<ItemSet, NumberSpinner>(); private final Map <ItemSet, SimpleCheckBox> setToggles = new TreeMap<ItemSet, SimpleCheckBox>(); private final Map <Slot, Integer> rows = new TreeMap<Slot, Integer>(); private final Map <Slot, SpecialItemType> selected = new TreeMap<Slot, SpecialItemType>(); private final Map <Slot, Map<String, BaseSpinner<?>>> attributeSpinners = new TreeMap<Slot, Map<String, BaseSpinner<?>>>(); private final NumberSpinner numAncients; private final SimpleCheckBox otherSets; private final Set<ItemsChangedListener> listeners = new TreeSet<ItemsChangedListener>(); private final FlexTable table2; private final FlexTable table3; public interface ItemsChangedListener { void itemsChanged(Map<Slot, ItemHolder> items); void setCountsChanged(Map<String, Integer> sets); } public ItemPanel() { disableListeners = false; SimpleCaptionPanel cptnpnlSpecialItems = new SimpleCaptionPanel("Items"); initWidget(cptnpnlSpecialItems); FlexTable table = new FlexTable(); cptnpnlSpecialItems.setContentWidget(table); SimpleCaptionPanel c1 = new SimpleCaptionPanel("General"); table.setWidget(0, 0, c1); table.getFlexCellFormatter().setVerticalAlignment(0, 0, HasVerticalAlignment.ALIGN_TOP); table1 = new FlexTable(); c1.setContentWidget(table1); SimpleCaptionPanel c3 = new SimpleCaptionPanel("Set Bonuses"); table.setWidget(1, 0, c3); table.getFlexCellFormatter().setVerticalAlignment(1, 0, HasVerticalAlignment.ALIGN_TOP); table3 = new FlexTable(); c3.setContentWidget(table3); SimpleCaptionPanel c2 = new SimpleCaptionPanel("Special Item Bonuses"); table.setWidget(0, 1, c2); table.getFlexCellFormatter().setRowSpan(0, 1, 2); table.getFlexCellFormatter().setVerticalAlignment(0, 1, HasVerticalAlignment.ALIGN_TOP); table2 = new FlexTable(); c2.setContentWidget(table2); int row = 0; Label label_4 = new Label("# Ancient Items:", false); table1.setWidget(row, 0, label_4); numAncients = new NumberSpinner(); numAncients.setVisibleLength(4); numAncients.setTitle("# Ancient items worn"); table1.setWidget(row, 1, numAncients); row++; Label label_2 = new Label("Area Damage +%:", false); table1.setWidget(row, 0, label_2); areaDamageEquipment = new NumberSpinner(); areaDamageEquipment.setMax(100); areaDamageEquipment.setVisibleLength(4); areaDamageEquipment.setTitle("Added Area Damage from Equipment only"); table1.setWidget(row, 1, areaDamageEquipment); row++; Label label_3 = new Label("Elite Damage +%:", false); table1.setWidget(row, 0, label_3); eliteDamagePercent = new NumberSpinner(); eliteDamagePercent.setVisibleLength(4); eliteDamagePercent.setTitle("Added Elite Damage from Armor and Weapons only (do not include Bane of the Powerful bonus)"); table1.setWidget(row, 1, eliteDamagePercent); row++; Label label_3a = new Label("Note: subtract 15% from the value displayed in-game if Bane of the Powerful is rank 25 or more.", true); label_3a.addStyleName("boldText"); label_3a.setWidth("350px"); table1.setWidget(row, 0, label_3a); table1.getFlexCellFormatter().setColSpan(row, 0, 2); row = 0; Label label_4b = new Label("Other Set Bonuses?", false); table3.setWidget(row, 0, label_4b); this.otherSets = new SimpleCheckBox(); this.otherSets.setTitle("Select if any set bonuses not listed below are in effect."); table3.setWidget(row, 1, this.otherSets); row++; for (ItemSet set : ItemSet.values()) { Anchor anchor = new Anchor(set.getName()); anchor.setWordWrap(false); anchor.setTarget("_blank"); anchor.setHref(set.getUrl()); table3.setWidget(row, 0, anchor); final ItemSet thisSet = set; if (set.getMaxPieces() > 2) { NumberSpinner number = new NumberSpinner(); number.setMax(set.getMaxPieces()); number.setVisibleLength(2); number.setTitle("# of pieces of this set worn (add 1 if using RoRG)"); table3.setWidget(row, 1, number); setCounts.put(thisSet, number); number.addChangeHandler(new ChangeHandler(){ @Override public void onChange(ChangeEvent event) { setChanged(thisSet); }}); } else { SimpleCheckBox checkBox = new SimpleCheckBox(); table3.setWidget(row, 1, checkBox); setToggles.put(thisSet, checkBox); checkBox.addClickHandler(new ClickHandler(){ @Override public void onClick(ClickEvent event) { setChanged(thisSet); }}); } row++; } Label label_4a = new Label("Note: Add 1 to Set Item count if using RROG", true); label_4a.addStyleName("boldText"); label_4a.setWidth("350px"); table3.setWidget(row, 0, label_4a); table3.getFlexCellFormatter().setColSpan(row, 0, 2); row = 0; for (Slot slot : Slot.values()) { List<SpecialItemType> types = SpecialItemType.getItemsBySlot(slot); if (!types.isEmpty()) { rows.put(slot, row); Anchor anchor = new Anchor(slot.getName()); anchor.setWordWrap(false); anchor.setHref("javascript:void(0)"); anchor.setTarget("_blank"); table2.setWidget(row, 0, anchor); anchors.put(slot, anchor); ListBox list = new ListBox(); list.setWidth("100%"); list.addItem("Other/None", ""); for (SpecialItemType t : types) { list.addItem(t.getName(), t.name()); } list.setSelectedIndex(0); final Slot thisSlot = slot; list.addChangeHandler(new ChangeHandler(){ @Override public void onChange(ChangeEvent event) { itemChanged(thisSlot); }}); listBoxes.put(slot, list); table2.setWidget(row, 1, list); attributeSpinners.put(slot, new TreeMap<String, BaseSpinner<?>>()); row += 2; } } } public void addItemsChangedListener(ItemsChangedListener l) { listeners.add(l); } public void removeItemsChangedListener(ItemsChangedListener l) { listeners.remove(l); } protected void setChanged(ItemSet set) { itemsChanged(null); if (!disableListeners) { Map<String, Integer> sets = this.getSetCounts(); for (ItemsChangedListener l : listeners) l.setCountsChanged(sets); } } protected void itemChanged(Slot slot) { SpecialItemType type = getSelectedItem(slot); boolean changed = setItem(slot, type, null); if ((type != null) && !slot.isCube()) { for (Slot s : type.getSlots()) { if ((s != slot) && !s.isCube()) { SpecialItemType other = getSelectedItem(s); if (other == type) { changed |= setItem(s, null, null); } } } } if (changed); itemsChanged(null); } private SpecialItemType getSelectedItem(Slot slot) { ListBox list = listBoxes.get(slot); int i = list.getSelectedIndex(); if (i <= 0) return null; else return SpecialItemType.valueOf(list.getValue(i)); } public NumberSpinner getEliteDamagePercent() { return eliteDamagePercent; } public NumberSpinner getAreaDamageEquipment() { return areaDamageEquipment; } public Map<Slot, ItemHolder> getItems() { Map<Slot, ItemHolder> map = new TreeMap<Slot, ItemHolder>(); for (Slot slot : Slot.values()) { if (this.listBoxes.containsKey(slot)) { SpecialItemType type = this.getSelectedItem(slot); if (type != null) { ItemHolder item = new ItemHolder(); AttributeData data = new AttributeData(); item.setType(type); item.setAttributes(data); map.put(slot, item); for (SpecialItemType.Attribute a : type.getAttributes()) { BaseSpinner<?> spinner = this.attributeSpinners.get(slot).get(a.getLabel()); if (spinner != null) { if (spinner instanceof NumberSpinner) { int n = ((NumberSpinner)spinner).getValue(); data.put(a.getLabel(), n); } else { double n = ((DoubleSpinner)spinner).getValue(); data.put(a.getLabel(), (int)(n * 10.0)); } } } } } } return map; } public void setItems(Map<Slot, ItemHolder> items) { this.disableListeners = true; boolean changed = false; Set<Slot> all = new TreeSet<Slot>(); for (Slot slot : Slot.values()) { if (this.listBoxes.containsKey(slot)) all.add(slot); } for (Map.Entry<Slot, ItemHolder> e : items.entrySet()) { Slot slot = e.getKey(); all.remove(slot); ItemHolder item = e.getValue(); changed |= setItem(slot, item.getType(), item.getAttributes()); } for (Slot slot : all) { changed |= setItem(slot, null, null); } this.disableListeners = false; if (changed) itemsChanged(null); } private boolean setItem(final Slot slot, SpecialItemType type, AttributeData data) { boolean changed = false; SpecialItemType prev = selected.get(slot); selected.put(slot, type); int row = rows.get(slot); if ((prev != null) && (type != prev)) { table2.removeCell(row+1, 1); attributeSpinners.get(slot).clear(); changed |= true; } if (type != prev) setItem(slot, type); if ((type != null) && (type != prev)) { changed |= true; FlexTable aTable = new FlexTable(); table2.setWidget(row+1, 1, aTable); SpecialItemType.Attribute[] aList = type.getAttributes(); int n = 0; for (SpecialItemType.Attribute a : aList) { String name = a.getLabel(); Label label = new Label(name + ":", false); aTable.setWidget(n, 0, label); double s = a.getScalar(); if (s > 100) { DoubleSpinner spinner = new DoubleSpinner(); spinner.setIncrement(0.1); spinner.setMin(a.getMin() / 10.0); spinner.setMax(a.getMax() / 10.0); spinner.setVisibleLength(6); aTable.setWidget(n, 1, spinner); attributeSpinners.get(slot).put(name, spinner); final SpecialItemType.Attribute thisAttribute = a; spinner.addChangeHandler(new ChangeHandler(){ @Override public void onChange(ChangeEvent event) { attributeValueChanged(slot, thisAttribute); }}); } else { NumberSpinner spinner = new NumberSpinner(); spinner.setMin(a.getMin()); spinner.setMax(a.getMax()); spinner.setVisibleLength(6); aTable.setWidget(n, 1, spinner); attributeSpinners.get(slot).put(name, spinner); final SpecialItemType.Attribute thisAttribute = a; spinner.addChangeHandler(new ChangeHandler(){ @Override public void onChange(ChangeEvent event) { attributeValueChanged(slot, thisAttribute); }}); } n++; } } if (type != null) { SpecialItemType.Attribute[] aList = type.getAttributes(); for (SpecialItemType.Attribute a : aList) { BaseSpinner<?> spinner = attributeSpinners.get(slot).get(a.getLabel()); Integer value = (data != null) ? data.get(a.getLabel()) : null; if (value == null) { if (slot.isCube()) { // TODO Get Min value for others that need min value as default for cube if (type == SpecialItemType.MeticulousBolts) value = a.getMin(); else value = a.getMax(); } else { // TODO Get Min value for others that need max value as default if (type == SpecialItemType.MeticulousBolts) value = a.getMax(); else value = a.getMin(); } } if (spinner instanceof NumberSpinner) { if (value != ((NumberSpinner)spinner).getValue()) { ((NumberSpinner)spinner).setValue(value); changed = true; } } else { if (value != (int)(((DoubleSpinner)spinner).getValue() * 10.0)) { ((DoubleSpinner)spinner).setValue(value / 10.0); changed = true; } } } } return changed; } protected void attributeValueChanged(Slot slot, Attribute thisAttribute) { itemsChanged(null); } private void setItem(Slot slot, SpecialItemType type) { ListBox list = listBoxes.get(slot); if (list != null) { if (type == null) { list.setSelectedIndex(0); anchors.get(slot).setHref("javascript:void(0)"); } else { int n = list.getItemCount(); anchors.get(slot).setHref(type.getUrl()); for (int i = 0; i < n; i++) { if (list.getValue(i).equals(type.name())) { list.setSelectedIndex(i); return; } } } } } protected void itemsChanged(ChangeEvent event) { if (!disableListeners) { for (ChangeHandler h : handlers) h.onChange(event); Map<Slot, ItemHolder> items = this.getItems(); for (ItemsChangedListener l : listeners) l.itemsChanged(items); } } public void addChangeHandler(ChangeHandler handler) { this.handlers.add(handler); } public int getNumNats() { return getSetCount(ItemSet.Nats); } private int getSetCount(ItemSet set) { if (set.getMaxPieces() > 2) { return this.setCounts.get(set).getValue(); } else { return this.setToggles.get(set).getValue() ? 2 : 0; } } public boolean isTnt() { return (this.getSelectedItem(Slot.Hands) == SpecialItemType.TnT) || (this.getSelectedItem(Slot.CubeArmor) == SpecialItemType.TnT); } public double getTntPercent() { if (!isTnt()) { return 0; } if (this.getSelectedItem(Slot.Hands) == SpecialItemType.TnT) return getItemAttributeValue(Slot.Hands, SpecialItemType.TnT.getAttributes()[0]); else return getItemAttributeValue(Slot.CubeArmor, SpecialItemType.TnT.getAttributes()[0]); } private double getItemAttributeValue(Slot slot, Attribute attribute) { BaseSpinner<?> spinner = this.attributeSpinners.get(slot).get(attribute.getLabel()); if (spinner == null) { if (attribute.getScalar() > 100.0) { return attribute.getMin() / 10.0; } else { return attribute.getMin(); } } if (spinner instanceof NumberSpinner) { return attribute.getRawAttributeValue(((NumberSpinner)spinner).getValue()); } else { return attribute.getRawAttributeValue((int)(((DoubleSpinner)spinner).getValue() * 10.0)); } } public boolean isPridesFall() { return (this.getSelectedItem(Slot.Head) == SpecialItemType.PridesFall) || (this.getSelectedItem(Slot.CubeArmor) == SpecialItemType.PridesFall); } public int getNumCrimson() { return getSetCount(ItemSet.Crimson); } public int getNumBorns() { return getSetCount(ItemSet.Borns); } public boolean isLeorics() { return (this.getSelectedItem(Slot.Head) == SpecialItemType.Leorics) || (this.getSelectedItem(Slot.CubeArmor) == SpecialItemType.Leorics); } public double getLeoricsPercent() { if (!isLeorics()) { return 0; } if (this.getSelectedItem(Slot.Head) == SpecialItemType.Leorics) return getItemAttributeValue(Slot.Head, SpecialItemType.Leorics.getAttributes()[0]); else return getItemAttributeValue(Slot.CubeArmor, SpecialItemType.Leorics.getAttributes()[0]); } public Map<String, Integer> getSetCounts() { Map<String, Integer> map = new TreeMap<String, Integer>(); for (ItemSet set : ItemSet.values()) { int n = getSetCount(set); if (n > 0) map.put(set.getSlug(), n); } return map; } public void setSetCounts(Map<String, Integer> map) { this.disableListeners = true; boolean changed = false; for (ItemSet set : ItemSet.values()) { Integer n = map.get(set.getSlug()); if (n == null) n = 0; n = Math.min(set.getMaxPieces(), n); int prev = getSetCount(set); if (prev != n) { setSetCount(set, n); changed = true; } } this.disableListeners = false; if (changed) this.itemsChanged(null); } private void setSetCount(ItemSet set, Integer n) { if (n == null) n = 0; if (set.getMaxPieces() > 2) { this.setCounts.get(set).setValue(n); } else { this.setToggles.get(set).setValue(n >= 2); } } public boolean isHuntersWrath() { return (this.getSelectedItem(Slot.Waist) == SpecialItemType.HuntersWrath) || (this.getSelectedItem(Slot.CubeArmor) == SpecialItemType.HuntersWrath); } public boolean isYangs() { return (this.getSelectedItem(Slot.MainHand) == SpecialItemType.Yangs) || (this.getSelectedItem(Slot.CubeWeapon) == SpecialItemType.Yangs); } public NumberSpinner getNumAncients() { return numAncients; } public SimpleCheckBox getOtherSets() { return otherSets; } }