Przeglądaj źródła

Completing the equip/unequip settings in the inventory

Sam Jaffe 9 lat temu
rodzic
commit
c5a461e05b

+ 6 - 2
resources/Potato.json

@@ -55,13 +55,16 @@
       {
         "name":"MWK Quarterstaff",
         "count":1,
+        "countEquipped":1,
         "value":{"pp":0,"gp":600,"sp":0,"cp":0},
-        "page":"???",
+        "page":"PH116",
         "slot":"TWO_HANDS",
+        "weight":4,
         "weapon":{
           "attackBonus":1,
           "damageBonus":0,
           "damage":"1d6",
+          "secondaryDamage":"1d6",
           "criticalThreat":20,
           "criticalDamage":2,
           "range":"Melee",
@@ -71,8 +74,9 @@
       {
         "name":"+1 Full Plate Armor",
         "count":1,
+        "countEquipped":1,
         "value":{"pp":0,"gp":2650,"sp":0,"cp":0},
-        "page":"???",
+        "page":"PH123",
         "slot":"BODY",
         "weight":50,
         "armor":{

+ 7 - 2
src/org/leumasjaffe/charsheet/model/inventory/DDInventory.java

@@ -7,17 +7,21 @@ import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 
+import org.leumasjaffe.observer.Observable;
+
 import com.fasterxml.jackson.annotation.JsonGetter;
 import com.fasterxml.jackson.annotation.JsonSetter;
 
 import lombok.AccessLevel;
 import lombok.Data;
+import lombok.EqualsAndHashCode;
 import lombok.NonNull;
 import lombok.experimental.FieldDefaults;
 
 @Data
+@EqualsAndHashCode(callSuper=false)
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
-public class DDInventory {
+public class DDInventory extends Observable {
 	
 	@NonNull List<DDItem> items;
 	@NonNull Map<EquipmentSlot, DDItem> equipment;
@@ -62,7 +66,7 @@ public class DDInventory {
 		case RING: return canEquip( EquipmentSlot.RING1 ) || 
 				canEquip( EquipmentSlot.RING2 );
 		default:
-			return equipment.containsKey(slot);
+			return !equipment.containsKey(slot);
 		}
 	}
 	
@@ -94,6 +98,7 @@ public class DDInventory {
 			break;
 		default:
 			equipment.remove(slot);
+			break;
 		}
 	}
 

+ 6 - 0
src/org/leumasjaffe/charsheet/model/inventory/DDItem.java

@@ -23,6 +23,8 @@ import lombok.experimental.FieldDefaults;
 public class DDItem {
 	String name = "";
 	IntValue count = new IntValue(1);
+	int countEquipped = 0;
+	IntValue weight = new IntValue(0);
 	Money value = new Money(0, 0, 0, 0);
 	StringValue page = new StringValue();
 	EquipmentSlot slot = EquipmentSlot.NONE;
@@ -48,4 +50,8 @@ public class DDItem {
 	private Map<String, Object> getProperties() { 
 		return Collections.unmodifiableMap(properties);
 	}
+	
+	public int getUnequippedCount() {
+		return count.value() - countEquipped;
+	}
 }

+ 6 - 0
src/org/leumasjaffe/charsheet/model/inventory/DDWeapon.java

@@ -10,13 +10,19 @@ public class DDWeapon {
 	public static enum Type { Piercing, Bludgeoning, Slashing }
 	int attackBonus;
 	String damage;
+	String secondaryDamage;
 	int damageBonus;
 	int criticalThreat;
 	int criticalDamage;
+	int secondaryCriticalDamage;
 	Range range;
 	Type type;
 	
 	public boolean hasCriticalThreat() {
 		return criticalThreat != 20 && criticalThreat != 0;
 	}
+
+	public boolean hasSecondaryAttack() {
+		return secondaryDamage != null && !secondaryDamage.isEmpty();
+	}
 }

+ 89 - 0
src/org/leumasjaffe/charsheet/view/inventory/EquipItemHelper.java

@@ -0,0 +1,89 @@
+package org.leumasjaffe.charsheet.view.inventory;
+
+import static org.leumasjaffe.charsheet.model.inventory.EquipmentSlot.*;
+
+import javax.swing.JOptionPane;
+
+import org.leumasjaffe.charsheet.model.inventory.DDInventory;
+import org.leumasjaffe.charsheet.model.inventory.DDItem;
+import org.leumasjaffe.charsheet.model.inventory.EquipmentSlot;
+import org.leumasjaffe.function.VoidVoidFunction;
+import org.leumasjaffe.observer.ObserverDispatch;
+
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+public class EquipItemHelper implements VoidVoidFunction {
+	final InventoryPanel panel;
+	final DDInventory inv;
+	final DDItem item;
+	
+	public void apply() {
+		if ( item.getUnequippedCount() == 0 ) { return; }
+		if ( inv.canEquip( item ) || getReplaceItem( item.getSlot() ) ) {
+			item.setCountEquipped(item.getCountEquipped() + 1);
+			inv.equipNext( item );
+			ObserverDispatch.notifySubscribers(inv, panel);
+		}
+	}
+
+	private boolean getReplaceItem(final EquipmentSlot slot) {
+		switch ( slot ) {
+		case TWO_HANDS: return selectToReplaceAllOf( TWO_HANDS, MAIN_HAND, OFF_HAND );
+		case ONE_HAND: return selectToReplaceOneOf( MAIN_HAND, OFF_HAND );
+		case RING: return selectToReplaceOneOf( RING1, RING2 );
+		default: return selectToReplace( slot );
+		}
+	}
+
+	private boolean selectToReplaceAllOf(final EquipmentSlot base,
+			final EquipmentSlot slot1, final EquipmentSlot slot2) {
+		final EquipItemPanel panel;
+		if ( inv.getEquipment().get(slot1).getSlot() == base ) {
+			panel = new EquipItemPanel("Do you want to replace the following item?", 
+					slot1, inv.getEquipment().get(slot1));
+		} else {
+			panel = new EquipItemPanel("Do you want to replace both of the following items?", 
+					new EquipItemPanel.Tuple(slot1, inv.getEquipment().get(slot1)),
+					new EquipItemPanel.Tuple(slot2, inv.getEquipment().get(slot2)));
+		}
+		if ( JOptionPane.showConfirmDialog(null, 
+				panel, "Replace Equipped Item", JOptionPane.YES_NO_OPTION) 
+				== JOptionPane.YES_OPTION ) {
+			inv.unequip( slot1 );
+			inv.unequip( slot2 );
+			return true;
+		}
+		return false;
+	}
+
+	private boolean selectToReplaceOneOf(final EquipmentSlot slot1, 
+			final EquipmentSlot slot2) {
+		final int choice = JOptionPane.showOptionDialog(null, 
+				new EquipItemPanel("Which of the following items would you like to replace?", 
+						new EquipItemPanel.Tuple(slot1, inv.getEquipment().get(slot1)),
+						new EquipItemPanel.Tuple(slot2, inv.getEquipment().get(slot2))), 
+				"Replace Equipped Item", JOptionPane.YES_NO_CANCEL_OPTION,
+				JOptionPane.QUESTION_MESSAGE, null, 
+				new String[] {"Cancel", slot1.toString(), slot2.toString()}, null );
+		if ( choice == JOptionPane.YES_OPTION ) {
+			inv.unequip( slot1 );
+			return true;
+		} else if ( choice == JOptionPane.NO_OPTION ) {
+			inv.unequip( slot2 );
+			return true;
+		}
+		return false;
+	}
+
+	private boolean selectToReplace(final EquipmentSlot slot) {
+		if ( JOptionPane.showConfirmDialog(null, 
+				new EquipItemPanel("Do you want to replace the following item?", slot, inv.getEquipment().get(slot)), 
+				"Replace Equipped Item", JOptionPane.YES_NO_OPTION) 
+				== JOptionPane.YES_OPTION ) {
+			inv.unequip( slot );
+			return true;
+		}
+		return false;
+	}
+}

+ 77 - 0
src/org/leumasjaffe/charsheet/view/inventory/EquipItemPanel.java

@@ -0,0 +1,77 @@
+package org.leumasjaffe.charsheet.view.inventory;
+
+import javax.swing.JPanel;
+import java.awt.GridBagLayout;
+import java.awt.GridBagConstraints;
+import javax.swing.JLabel;
+import javax.swing.JTextField;
+import java.awt.Insets;
+import java.awt.Color;
+import javax.swing.SwingConstants;
+
+import org.jdesktop.swingx.VerticalLayout;
+import org.leumasjaffe.charsheet.model.inventory.DDItem;
+import org.leumasjaffe.charsheet.model.inventory.EquipmentSlot;
+
+import lombok.AllArgsConstructor;
+
+import java.awt.Font;
+
+public class EquipItemPanel extends JPanel {
+	@AllArgsConstructor
+	public static class Tuple { EquipmentSlot slot; DDItem item; }
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 1L;
+	public EquipItemPanel(final String title, final EquipmentSlot slot, final DDItem item) {
+		this(title, new Tuple(slot, item));
+	}
+	
+	public EquipItemPanel(final String title, final Tuple... tuples) {
+		setOpaque(false);
+		setLayout(new VerticalLayout(5));
+
+		JLabel label = new JLabel(title);
+		label.setOpaque(true);
+		label.setBackground(Color.BLACK);
+		label.setFont(new Font("Tahoma", Font.BOLD, 20));
+		label.setHorizontalAlignment(SwingConstants.CENTER);
+		label.setForeground(Color.WHITE);
+		add(label);
+		for (int i = 0; i < tuples.length; ++i) {
+			JPanel panel = new JPanel();
+			panel.setOpaque(false);
+			add(panel);
+			GridBagLayout gbl_panel = new GridBagLayout();
+			gbl_panel.columnWidths = new int[]{0, 0, 0};
+			gbl_panel.rowHeights = new int[]{0, 0};
+			gbl_panel.columnWeights = new double[]{0.0, 1.0, Double.MIN_VALUE};
+			gbl_panel.rowWeights = new double[]{0.0, Double.MIN_VALUE};
+			panel.setLayout(gbl_panel);
+
+			JLabel lblSlot = new JLabel(tuples[i].slot.toString());
+			lblSlot.setOpaque(true);
+			lblSlot.setBackground(Color.BLACK);
+			lblSlot.setFont(new Font("Tahoma", Font.BOLD, 16));
+			lblSlot.setHorizontalAlignment(SwingConstants.CENTER);
+			lblSlot.setForeground(Color.WHITE);
+			GridBagConstraints gbc_lblSlot = new GridBagConstraints();
+			gbc_lblSlot.fill = GridBagConstraints.HORIZONTAL;
+			gbc_lblSlot.insets = new Insets(0, 0, 0, 5);
+			gbc_lblSlot.gridx = 0;
+			gbc_lblSlot.gridy = 0;
+			panel.add(lblSlot, gbc_lblSlot);
+
+			JTextField nameField = new JTextField(tuples[i].item.getName());
+			nameField.setEditable(false);
+			GridBagConstraints gbc_nameField = new GridBagConstraints();
+			gbc_nameField.anchor = GridBagConstraints.WEST;
+			gbc_nameField.gridx = 1;
+			gbc_nameField.gridy = 0;
+			panel.add(nameField, gbc_nameField);
+			nameField.setColumns(10);
+		}
+	}
+
+}

+ 41 - 14
src/org/leumasjaffe/charsheet/view/inventory/EquipmentPanel.java

@@ -12,6 +12,8 @@ import org.leumasjaffe.charsheet.model.DDCharacter;
 import org.leumasjaffe.charsheet.model.inventory.DDInventory;
 import org.leumasjaffe.charsheet.model.inventory.DDItem;
 import org.leumasjaffe.charsheet.model.inventory.EquipmentSlot;
+import org.leumasjaffe.observer.ObservableListener;
+import org.leumasjaffe.observer.ObserverDispatch;
 
 import lombok.AccessLevel;
 import lombok.experimental.FieldDefaults;
@@ -21,8 +23,12 @@ import java.awt.Font;
 import java.awt.GridBagConstraints;
 import java.awt.GridBagLayout;
 import java.awt.Insets;
+import java.util.EnumSet;
+import java.util.Set;
 import java.util.function.Function;
 
+import static org.leumasjaffe.charsheet.model.inventory.EquipmentSlot.*;
+
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
 public class EquipmentPanel extends JPanel {
 	/**
@@ -30,6 +36,8 @@ public class EquipmentPanel extends JPanel {
 	 */
 	private static final long serialVersionUID = 1L;
 	JPanel equipment;
+	
+	ObservableListener<EquipmentPanel, DDInventory> equipmentObserver;
 
 	public EquipmentPanel() {
 		GridBagLayout gridBagLayout = new GridBagLayout();
@@ -61,29 +69,45 @@ public class EquipmentPanel extends JPanel {
 		equipment.setBackground(Color.WHITE);
 		scrollPane.setViewportView(equipment);
 		equipment.setLayout(new VerticalLayout(5));
+		
+		equipmentObserver = new ObservableListener<>(this, 
+				(self, inv) -> self.updateModel(inv) );
 	}
 	
 	public void setModel(DDCharacter model) {
-		equipment.removeAll();
-		
-		final DDInventory inv = model.getInventory();
-		
-		final DDItem armor = inv.getEquipment().get(EquipmentSlot.BODY);
-		if ( armor != null ) {
-			createWithRightClickMenu(ArmorPanel::new, inv, EquipmentSlot.BODY);
+		equipmentObserver.setObserved(model.getInventory());
+	}
+
+	private void updateModel(final DDInventory inv) {
+		final Set<EquipmentSlot> manual = EnumSet.noneOf(EquipmentSlot.class);
+		equipment.removeAll();	
+		final DDItem armor = inv.getEquipment().get(BODY);
+		if ( armor != null && armor.isArmor() ) {
+			manual.add(BODY);
+			createWithRightClickMenu(ArmorPanel::new, inv, BODY);
 		}
 		
-		final DDItem main = inv.getEquipment().get(EquipmentSlot.MAIN_HAND);
-		final DDItem off = inv.getEquipment().get(EquipmentSlot.OFF_HAND);
+		final DDItem main = inv.getEquipment().get(MAIN_HAND);
+		final DDItem off = inv.getEquipment().get(OFF_HAND);
 		if ( off != null && off.isArmor() ) {
-			createWithRightClickMenu(ShieldPanel::new, inv, EquipmentSlot.OFF_HAND);
+			manual.add(OFF_HAND);
+			createWithRightClickMenu(ShieldPanel::new, inv, OFF_HAND);
 		}
-		if ( main != null ) {
-			createWithRightClickMenu(WeaponPanel::new, inv, EquipmentSlot.MAIN_HAND);
+		if ( main != null && main.isWeapon() ) {
+			manual.add(MAIN_HAND);
+			createWithRightClickMenu(WeaponPanel::new, inv, MAIN_HAND);
 		}
-		if ( off != null && ( off != main || couldDualWieldThis(off) ) ) {
-			createWithRightClickMenu(WeaponPanel::new, inv, EquipmentSlot.OFF_HAND);
+		if ( off != null && off.isWeapon() ) {
+			manual.add(OFF_HAND);
+			if ( off != main || couldDualWieldThis(off) ) {
+				createWithRightClickMenu(WeaponPanel::new, inv, OFF_HAND);
+			}
 		}
+		
+		inv.getEquipment().keySet().stream().filter( slot -> ! manual.contains(slot) )
+		.forEach( slot -> {
+			createWithRightClickMenu(null, inv, slot);
+		});
 	}
 
 	private boolean couldDualWieldThis(final DDItem item) {
@@ -98,7 +122,10 @@ public class EquipmentPanel extends JPanel {
 		panel.addMouseListener(new PopClickListener(
 				new EquipmentInfoMenu(item, () -> {
 					equipment.remove(panel);
+					item.setCountEquipped(item.getCountEquipped() - 1);
 					inv.unequip(slot);
+					repaint();
+					ObserverDispatch.notifySubscribers(inv, this);
 				})));
 	}
 }

+ 11 - 75
src/org/leumasjaffe/charsheet/view/inventory/InventoryPanel.java

@@ -2,7 +2,6 @@ package org.leumasjaffe.charsheet.view.inventory;
 
 import javax.swing.JComponent;
 import javax.swing.JLabel;
-import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
 import javax.swing.SwingConstants;
@@ -13,13 +12,11 @@ import org.leumasjaffe.charsheet.controller.inventory.PopClickListener;
 import org.leumasjaffe.charsheet.model.DDCharacter;
 import org.leumasjaffe.charsheet.model.inventory.DDInventory;
 import org.leumasjaffe.charsheet.model.inventory.DDItem;
-import org.leumasjaffe.charsheet.model.inventory.EquipmentSlot;
+import org.leumasjaffe.observer.ObservableListener;
 
 import lombok.AccessLevel;
 import lombok.experimental.FieldDefaults;
 
-import static org.leumasjaffe.charsheet.model.inventory.EquipmentSlot.*;
-
 import java.awt.Color;
 import java.awt.Font;
 import java.awt.GridBagConstraints;
@@ -32,6 +29,7 @@ public class InventoryPanel extends JPanel {
 	 */
 	private static final long serialVersionUID = 1L;
 	JComponent inventory;
+	ObservableListener<InventoryPanel, DDInventory> inventoryObserver;
 
 	public InventoryPanel() {
 		GridBagLayout gridBagLayout = new GridBagLayout();
@@ -62,89 +60,27 @@ public class InventoryPanel extends JPanel {
 		inventory.setBackground(Color.WHITE);
 		scrollPane.setViewportView(inventory);
 		inventory.setLayout(new VerticalLayout(5));
+		
+		inventoryObserver = new ObservableListener<>(this, 
+				(self, inv) -> self.updateModel(inv) );
 	}
 
-	public void setModel(DDCharacter model) {
+	private void updateModel(DDInventory inv) {
 		inventory.removeAll();
 		
-		final DDInventory inv = model.getInventory();
 		inv.getItems().stream().forEach( 
 				item -> createWithRightClickMenu(inv, item) );
 	}
 
+	public void setModel(DDCharacter model) {
+		inventoryObserver.setObserved(model.getInventory());
+	}
+
 	private void createWithRightClickMenu(final DDInventory inv, final DDItem item) {
 		final ItemPanel panel = new ItemPanel(item);
 		panel.addMouseListener(new PopClickListener(
-				new ItemInfoMenu(item)));
+				new ItemInfoMenu(item, new EquipItemHelper(this, inv, item))));
 		inventory.add(panel);
 	}
 	
-	void example_equipItem(final DDInventory inv, final DDItem item) {
-		if ( getUnequippedCount(item) == 0 ) { return; }
-		if ( inv.canEquip( item ) || getReplaceItem( inv, item.getSlot() ) ) {
-			inv.equipNext( item );
-		}
-	}
-
-	private boolean getReplaceItem(final DDInventory inv, 
-			final EquipmentSlot slot) {
-		switch ( slot ) {
-		case TWO_HANDS: return selectToReplaceAllOf( inv, MAIN_HAND, OFF_HAND );
-		case ONE_HAND: return selectToReplaceOneOf( inv, MAIN_HAND, OFF_HAND );
-		case RING: return selectToReplaceOneOf( inv, RING1, RING2 );
-		default: return selectToReplace( inv, slot );
-		}
-	}
-
-	private boolean selectToReplaceAllOf(final DDInventory inv, 
-			final EquipmentSlot slot1, final EquipmentSlot slot2) {
-		if ( inv.getEquipment().get(slot1) == inv.getEquipment().get(slot2) ) {
-			
-		} else {
-			
-		}
-		if ( JOptionPane.showConfirmDialog(null, 
-				"TODO", 
-				"Replace Equipped Item", JOptionPane.YES_NO_OPTION) 
-				== JOptionPane.YES_OPTION ) {
-			inv.unequip( slot1 );
-			inv.unequip( slot2 );
-			return true;
-		}
-		return false;
-	}
-
-	private boolean selectToReplaceOneOf(final DDInventory inv, 
-			final EquipmentSlot slot1, final EquipmentSlot slot2) {
-		final int choice = JOptionPane.showOptionDialog(null, 
-				"TODO", 
-				"Replace Equipped Item", JOptionPane.YES_NO_CANCEL_OPTION,
-				JOptionPane.QUESTION_MESSAGE, null, 
-				new String[] {"Cancel", slot1.toString(), slot2.toString()}, null );
-		if ( choice == JOptionPane.YES_OPTION ) {
-			inv.unequip( slot1 );
-			return true;
-		} else if ( choice == JOptionPane.NO_OPTION ) {
-			inv.unequip( slot2 );
-			return true;
-		}
-		return false;
-	}
-
-	private boolean selectToReplace(final DDInventory inv, 
-			final EquipmentSlot slot) {
-		if ( JOptionPane.showConfirmDialog(null, 
-				"TODO", 
-				"Replace Equipped Item", JOptionPane.YES_NO_OPTION) 
-				== JOptionPane.YES_OPTION ) {
-			inv.unequip( slot );
-			return true;
-		}
-		return false;
-	}
-
-	private int getUnequippedCount(DDItem item) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
 }

+ 4 - 2
src/org/leumasjaffe/charsheet/view/inventory/ItemInfoMenu.java

@@ -4,6 +4,7 @@ import javax.swing.JPopupMenu;
 
 import org.leumasjaffe.charsheet.model.inventory.DDItem;
 import org.leumasjaffe.charsheet.model.inventory.EquipmentSlot;
+import org.leumasjaffe.function.VoidVoidFunction;
 
 import javax.swing.JMenuItem;
 
@@ -13,13 +14,14 @@ class ItemInfoMenu extends JPopupMenu {
 	 */
 	private static final long serialVersionUID = 1L;
 
-	public ItemInfoMenu(DDItem item) {
+	public ItemInfoMenu(final DDItem item, final VoidVoidFunction cons) {
 		
 		JMenuItem mntmInfo = new JMenuItem("Info");
 		add(mntmInfo);
 		
-		if ( item.getSlot() != EquipmentSlot.NONE ) {
+		if ( item.getSlot() != EquipmentSlot.NONE && item.getUnequippedCount() > 0 ) {
 			JMenuItem mntmEquip = new JMenuItem("Equip");
+			mntmEquip.addActionListener(e -> cons.apply());
 			add(mntmEquip);
 		}
 		

+ 2 - 1
src/org/leumasjaffe/charsheet/view/inventory/ItemPanel.java

@@ -13,6 +13,7 @@ import java.awt.Color;
 import javax.swing.SwingConstants;
 
 import org.leumasjaffe.charsheet.model.inventory.DDItem;
+import org.leumasjaffe.charsheet.util.StringHelper;
 
 import java.awt.Component;
 import javax.swing.Box;
@@ -114,7 +115,7 @@ public class ItemPanel extends JPanel {
 		add(countField, gbc_countField);
 		countField.setColumns(10);
 		
-		JTextField weightField = new JTextField();
+		JTextField weightField = new JTextField(StringHelper.toString(item.getWeight().value()) + " lb.");
 		weightField.setHorizontalAlignment(SwingConstants.CENTER);
 		GridBagConstraints gbc_weightField = new GridBagConstraints();
 		gbc_weightField.insets = new Insets(0, 0, 0, 0);

+ 1 - 1
src/org/leumasjaffe/charsheet/view/inventory/ShieldPanel.java

@@ -132,7 +132,7 @@ public class ShieldPanel extends JPanel {
 		panel.add(armorBonusField, gbc_armorBonusField);
 		armorBonusField.setColumns(10);
 		
-		JTextField weightField = new JTextField();
+		JTextField weightField = new JTextField(StringHelper.toString(item.getWeight().value()) + " lb.");
 		weightField.setHorizontalAlignment(SwingConstants.CENTER);
 		GridBagConstraints gbc_weightField = new GridBagConstraints();
 		gbc_weightField.insets = new Insets(0, 0, 0, 0);

+ 13 - 2
src/org/leumasjaffe/charsheet/view/inventory/WeaponPanel.java

@@ -132,7 +132,13 @@ public class WeaponPanel extends JPanel {
 		panel.add(attackBonusField, gbc_attackBonusField);
 		attackBonusField.setColumns(10);
 		
-		JTextField damageField = new JTextField(weapon.getDamage() + StringHelper.toSignedString(weapon.getDamageBonus(), 0));
+		final String dmgBonus = StringHelper.toSignedString(weapon.getDamageBonus(), 0);
+		StringBuilder dmgStr = new StringBuilder();
+		dmgStr.append(weapon.getDamage()).append(dmgBonus);
+		if ( weapon.hasSecondaryAttack() ) {
+			dmgStr.append('/').append(weapon.getSecondaryDamage()).append(dmgBonus);
+		}
+		JTextField damageField = new JTextField(dmgStr.toString());
 		damageField.setHorizontalAlignment(SwingConstants.CENTER);
 		GridBagConstraints gbc_damageField = new GridBagConstraints();
 		gbc_damageField.insets = new Insets(0, 0, 0, 0);
@@ -143,8 +149,13 @@ public class WeaponPanel extends JPanel {
 		damageField.setColumns(10);
 		
 		final StringBuilder critStr = new StringBuilder();
-		if (weapon.hasCriticalThreat()) { critStr.append(weapon.getCriticalThreat()).append("-20/"); }
+		if (weapon.hasCriticalThreat()) { 
+			critStr.append(weapon.getCriticalThreat()).append("-20/");
+		}
 		critStr.append('x').append(weapon.getCriticalDamage());
+		if (weapon.hasSecondaryAttack() && weapon.getSecondaryCriticalDamage() != 0) {
+			critStr.append("/x").append(weapon.getSecondaryCriticalDamage());
+		}
 		JTextField criticalField = new JTextField(critStr.toString());
 		criticalField.setHorizontalAlignment(SwingConstants.CENTER);
 		GridBagConstraints gbc_criticalField = new GridBagConstraints();

+ 95 - 19
src/org/leumasjaffe/charsheet/view/summary/ArmorLine.java

@@ -6,28 +6,39 @@ import javax.swing.JLabel;
 import java.awt.GridBagConstraints;
 import java.awt.Color;
 import javax.swing.border.LineBorder;
+
+import org.leumasjaffe.charsheet.model.AbilityScores;
+import org.leumasjaffe.charsheet.model.DDCharacter;
+import org.leumasjaffe.charsheet.model.inventory.DDInventory;
+import org.leumasjaffe.charsheet.model.inventory.DDItem;
+import org.leumasjaffe.charsheet.model.inventory.EquipmentSlot;
+import org.leumasjaffe.charsheet.model.observable.IntValue;
+import org.leumasjaffe.charsheet.util.StringHelper;
+import org.leumasjaffe.observer.IndirectObservableListener;
+import org.leumasjaffe.observer.ObservableListener;
+
+import lombok.AccessLevel;
+import lombok.experimental.FieldDefaults;
+
 import java.awt.Font;
 import javax.swing.SwingConstants;
 import java.awt.Dimension;
 import javax.swing.JTextField;
 import java.awt.Insets;
 
+@FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
 public class ArmorLine extends JPanel {
 	/**
 	 * 
 	 */
 	private static final long serialVersionUID = 1L;
-	private JTextField total;
-	private JTextField deflection;
-	private JTextField armor;
-	private JTextField shield;
-	private JTextField dexterity;
-	private JTextField size;
-	private JTextField natural;
-	private JTextField misc;
-	private JTextField touch;
-	private JTextField flatFoot;
 
+	IndirectObservableListener<JTextField, DDCharacter> armorTotalObserver;
+	ObservableListener<JTextField, DDInventory> armorArmorObserver;
+	ObservableListener<JTextField, DDInventory> armorShieldObserver;
+	IndirectObservableListener<JTextField, DDCharacter> armorDexObserver;
+	private JTextField size;
+	
 	public ArmorLine() {
 		setMinimumSize(new Dimension(400, 46));
 		setMaximumSize(new Dimension(400, 46));
@@ -73,7 +84,7 @@ public class ArmorLine extends JPanel {
 		gbc_lblAc.gridy = 0;
 		panel.add(lblAc, gbc_lblAc);
 		
-		total = new JTextField();
+		JTextField total = new JTextField();
 		total.setToolTipText("Total AC");
 		total.setPreferredSize(new Dimension(30, 20));
 		total.setMinimumSize(new Dimension(30, 20));
@@ -105,7 +116,7 @@ public class ArmorLine extends JPanel {
 		gbc_label_7.gridy = 0;
 		panel.add(label_7, gbc_label_7);
 		
-		armor = new JTextField();
+		JTextField armor = new JTextField();
 		armor.setToolTipText("Armor Bonus");
 		armor.setPreferredSize(new Dimension(25, 20));
 		armor.setMinimumSize(new Dimension(25, 20));
@@ -129,7 +140,7 @@ public class ArmorLine extends JPanel {
 		gbc_label_1.gridy = 0;
 		panel.add(label_1, gbc_label_1);
 		
-		shield = new JTextField();
+		JTextField shield = new JTextField();
 		shield.setToolTipText("Shield Bonus");
 		shield.setPreferredSize(new Dimension(25, 20));
 		shield.setMinimumSize(new Dimension(25, 20));
@@ -153,7 +164,7 @@ public class ArmorLine extends JPanel {
 		gbc_label_2.gridy = 0;
 		panel.add(label_2, gbc_label_2);
 		
-		dexterity = new JTextField();
+		JTextField dexterity = new JTextField();
 		dexterity.setToolTipText("Dexterity Modifier");
 		dexterity.setPreferredSize(new Dimension(25, 20));
 		dexterity.setMinimumSize(new Dimension(25, 20));
@@ -201,7 +212,7 @@ public class ArmorLine extends JPanel {
 		gbc_label_4.gridy = 0;
 		panel.add(label_4, gbc_label_4);
 		
-		natural = new JTextField();
+		JTextField natural = new JTextField();
 		natural.setToolTipText("Natural Armor");
 		natural.setPreferredSize(new Dimension(25, 20));
 		natural.setMinimumSize(new Dimension(25, 20));
@@ -225,7 +236,7 @@ public class ArmorLine extends JPanel {
 		gbc_label_5.gridy = 0;
 		panel.add(label_5, gbc_label_5);
 		
-		deflection = new JTextField();
+		JTextField deflection = new JTextField();
 		deflection.setToolTipText("Deflection Modifier");
 		deflection.setPreferredSize(new Dimension(25, 20));
 		deflection.setMinimumSize(new Dimension(25, 20));
@@ -249,7 +260,7 @@ public class ArmorLine extends JPanel {
 		gbc_label_6.gridy = 0;
 		panel.add(label_6, gbc_label_6);
 		
-		misc = new JTextField();
+		JTextField misc = new JTextField();
 		misc.setToolTipText("Miscellaneous Modifier");
 		misc.setPreferredSize(new Dimension(25, 20));
 		misc.setMinimumSize(new Dimension(25, 20));
@@ -296,7 +307,7 @@ public class ArmorLine extends JPanel {
 		gbc_lblTouch.gridy = 0;
 		panel_1.add(lblTouch, gbc_lblTouch);
 		
-		touch = new JTextField();
+		JTextField touch = new JTextField();
 		touch.setToolTipText("Touch AC");
 		touch.setPreferredSize(new Dimension(30, 20));
 		touch.setMinimumSize(new Dimension(30, 20));
@@ -330,7 +341,7 @@ public class ArmorLine extends JPanel {
 		gbc_lblFlatfooted.gridy = 0;
 		panel_1.add(lblFlatfooted, gbc_lblFlatfooted);
 		
-		flatFoot = new JTextField();
+		JTextField flatFoot = new JTextField();
 		flatFoot.setToolTipText("Flat-Footed AC");
 		flatFoot.setPreferredSize(new Dimension(30, 20));
 		flatFoot.setMinimumSize(new Dimension(30, 20));
@@ -344,6 +355,71 @@ public class ArmorLine extends JPanel {
 		gbc_flatFoot.gridx = 3;
 		gbc_flatFoot.gridy = 0;
 		panel_1.add(flatFoot, gbc_flatFoot);
+		
+		armorTotalObserver = new IndirectObservableListener<>(total, (c, v) -> {
+			final DDInventory inv = v.getInventory();
+			int iarmor = 0;
+			int ishield = 0;
+			int dex = AbilityScores.modifier(v.getAbilities().getBase().getDex().value());
+			int isize = v.getSize().value().modifier;
+			int inatural = 0;
+			int ideflect = 0;
+			int imisc = 0;
+			{
+				final DDItem body = inv.getEquipment().get(EquipmentSlot.BODY);
+				if ( body != null && body.isArmor() ) {
+					iarmor = body.getArmor().getBonus();
+					dex = Math.min(dex, body.getArmor().getMaxDex());
+				}
+			}
+			{
+				final DDItem offHand = inv.getEquipment().get(EquipmentSlot.OFF_HAND);
+				if ( offHand != null && offHand.isArmor() ) {
+					ishield = offHand.getArmor().getBonus();
+				}
+			}
+			c.setText(StringHelper.toString(10 + iarmor + ishield + 
+					dex + isize + inatural + ideflect + imisc));
+		});
+		
+		armorArmorObserver = new ObservableListener<>(armor, (c, v) -> {
+			int iarmor = 0;
+			final DDItem body = v.getEquipment().get(EquipmentSlot.BODY);
+			if ( body != null && body.isArmor() ) {
+				iarmor = body.getArmor().getBonus();
+			}
+			c.setText(StringHelper.toString(iarmor));
+		});
+		
+		armorShieldObserver = new ObservableListener<>(shield, (c, v) -> {
+			int iarmor = 0;
+			final DDItem offHand = v.getEquipment().get(EquipmentSlot.OFF_HAND);
+			if ( offHand != null && offHand.isArmor() ) {
+				iarmor = offHand.getArmor().getBonus();
+			}
+			c.setText(StringHelper.toString(iarmor));
+		});
+		
+		armorDexObserver = new IndirectObservableListener<>(dexterity, (c, v) -> {
+			final DDInventory inv = v.getInventory();
+			int dex = AbilityScores.modifier(v.getAbilities().getBase().getDex().value());
+			{
+				final DDItem body = inv.getEquipment().get(EquipmentSlot.BODY);
+				if ( body != null && body.isArmor() ) {
+					dex = Math.min(dex, body.getArmor().getMaxDex());
+				}
+			}
+			c.setText(StringHelper.toString(dex));
+		});
+	}
+	
+	public void setModel(final DDCharacter model) {
+		final DDInventory inv = model.getInventory();
+		final IntValue dex = model.getAbilities().getBase().getDex();
+		armorTotalObserver.setObserved(model, inv, dex);
+		armorArmorObserver.setObserved(inv);
+		armorShieldObserver.setObserved(inv);
+		armorDexObserver.setObserved(model, inv, dex);
 	}
 
 }