Jelajahi Sumber

Add handling for Wizard spell book rules

Sam Jaffe 8 tahun lalu
induk
melakukan
07e2201a51

+ 4 - 0
src/main/lombok/org/leumasjaffe/charsheet/model/magic/DDSpellbook.java

@@ -3,6 +3,7 @@ package org.leumasjaffe.charsheet.model.magic;
 import java.util.Collection;
 import java.util.List;
 
+import org.leumasjaffe.charsheet.model.observable.IntValue;
 import org.leumasjaffe.observer.Observable;
 
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
@@ -19,6 +20,9 @@ public abstract class DDSpellbook extends Observable {
 
 	public boolean learnsSpells() { return false; }
 	public boolean preparesSpells() { return false; }
+	public IntValue getSharedAllowedSlots() {
+		return new IntValue(-1);
+	}
 	
 	public int numSpellsKnownAtLevel( int level ) {
 		return spellsKnownAtLevel( level ).size();

+ 1 - 0
src/main/lombok/org/leumasjaffe/charsheet/model/magic/impl/Researched.java

@@ -38,6 +38,7 @@ public class Researched extends Prepared {
 	@NonNull Map<Integer, Researched.Level> spellInfo;
 	@NonNull ClassReference classRef;
 
+	@Override
 	public IntValue getSharedAllowedSlots() {
 		return new IntValue(freeSpellsPerLevel);
 	}

+ 40 - 11
src/main/lombok/org/leumasjaffe/charsheet/view/level/LevelUpSpellPanel.java

@@ -4,12 +4,14 @@ import javax.swing.JPanel;
 
 import org.jdesktop.swingx.VerticalLayout;
 import org.leumasjaffe.charsheet.model.magic.DDSpell;
+import org.leumasjaffe.charsheet.model.observable.IntValue;
 import org.leumasjaffe.charsheet.view.level.UpdateClassWithLevelPanel.BoolArray;
 import org.leumasjaffe.charsheet.view.magic.SelectSpellsPanel;
 import org.leumasjaffe.charsheet.view.magic.SelectSpellsPanel.Info;
 import org.leumasjaffe.observer.ObserverDispatch;
 
 import lombok.AccessLevel;
+import lombok.Getter;
 import lombok.experimental.FieldDefaults;
 import lombok.experimental.NonFinal;
 
@@ -19,6 +21,8 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
 
 import javax.swing.JScrollPane;
 import java.awt.GridBagConstraints;
@@ -58,9 +62,15 @@ class LevelUpSpellPanel extends JPanel {
 
 	int[] ready = {0};
 	@NonFinal int spellLevelsGrown = 0;
-	int oldHighestSpellLevel, newHighestSpellLevel;
+	int oldHighestSpellLevel, newHighestSpellLevel, toLevel;
+	SpellPickType pick;
+	SelectSpellsPanel.Info info;
+	@Getter List<SelectSpellsPanel> panels;
 
 	public LevelUpSpellPanel(SpellPickType pick, SelectSpellsPanel.Info info, int toLevel, BoolArray readyCount) {
+		this.pick = pick;
+		this.info = info;
+		this.toLevel = toLevel;
 		newHighestSpellLevel = info.dclass.getHighestSpellLevel(toLevel);
 		oldHighestSpellLevel = info.dclass.getHighestSpellLevel();
 
@@ -81,18 +91,17 @@ class LevelUpSpellPanel extends JPanel {
 		JPanel panel = new JPanel(new VerticalLayout(5));
 		scrollPane.setViewportView(panel);
 
-		final List<SelectSpellsPanel> panels = new ArrayList<>();
-		final List<List<Integer>> spellList = pick.getSpellCounts(info);
-		final List<Integer> spellsAtPreviousLevel = toLevel == 1 ? Collections.emptyList() :
-			spellList.get(toLevel-2);
-		final List<Integer> spellsAtCurrentLevel = spellList.get(toLevel-1);
+		panels = new ArrayList<>();
+
+		final IntValue val = getSharedAllowedSlots(info);
+		final Map<Integer, Integer> spells = getNewSpells(val);
+		final int sharedSlots = val.value();
 		for (int i = 0; i < newHighestSpellLevel; ++i) {
+			if (spells.get(i) < 0) continue;
 			++spellLevelsGrown;
-			final int newSpells = diff(spellsAtCurrentLevel, spellsAtPreviousLevel, i,
-					isNewSpellCircle(i));
 			SelectSpellsPanel lvl = new SelectSpellsPanel(info, i,
-					new LinkedHashSet<>(), newSpells, pick.getAvailableSpells(info, i),
-					pick != SpellPickType.LEARN);
+					new LinkedHashSet<>(), Math.max(spells.get(i), sharedSlots), pick.getAvailableSpells(info, i),
+					pick != SpellPickType.LEARN, val);
 			panels.add(lvl);
 			lvl.addPropertyChangeListener(SelectSpellsPanel.READY, e -> {
 				if ((Boolean) e.getNewValue()) ++ready[0];
@@ -103,13 +112,33 @@ class LevelUpSpellPanel extends JPanel {
 			panel.add(lvl);
 		}
 	}
+	
+	private Map<Integer, Integer> getNewSpells(IntValue sharedSpellCountLimit) {
+		final Map<Integer, Integer> map = new TreeMap<>();
+		final List<List<Integer>> spellList = pick.getSpellCounts(info);
+		final List<Integer> spellsAtPreviousLevel = toLevel == 1 ? Collections.emptyList() :
+			spellList.get(toLevel-2);
+		final List<Integer> spellsAtCurrentLevel = spellList.get(toLevel-1);
+		for (int i = 0; i < newHighestSpellLevel; ++i) {
+			map.put(i, diff(spellsAtCurrentLevel, spellsAtPreviousLevel, i,
+					isNewSpellCircle(i)));
+		}
+		if (!map.isEmpty()) sharedSpellCountLimit.value(-1);
+		return map;
+	}
+
+	private IntValue getSharedAllowedSlots(Info info) {
+		return info.dclass.getSpellBook().get().getSharedAllowedSlots();
+	}
 
 	private boolean isNewSpellCircle(int i) {
 		return i == (newHighestSpellLevel - 1) && oldHighestSpellLevel != newHighestSpellLevel;
 	}
 
 	private int diff(List<Integer> current, List<Integer> previous, int level, boolean newSpellLevel) {
-		return current.get(level) - (newSpellLevel || previous.size() <= level ? 0 : previous.get(level));
+		if (current.size() <= level) return 0;
+		else if (newSpellLevel || previous.size() <= level) return current.get(level);
+		else return current.get(level) - previous.get(level);
 	}
 
 }

+ 1 - 0
src/main/lombok/org/leumasjaffe/charsheet/view/level/UpdateClassWithLevelPanel.java

@@ -73,6 +73,7 @@ class UpdateClassWithLevelPanel extends JPanel {
 						info.toLevel, readyCount);
 				tabbedPane.addTab("Learn Spells", null, spells, null);
 			}
+			// FIXME: needs to get the newly learned spells
 			if (sb.preparesSpells()) {
 				JPanel spells = new LevelUpSpellPanel(LevelUpSpellPanel.SpellPickType.PREPARE,
 						new SelectSpellsPanel.Info(info.ddCharacter, info.ddClass),

+ 2 - 2
src/main/lombok/org/leumasjaffe/charsheet/view/magic/SelectPreparedSpellsPanel.java

@@ -12,8 +12,8 @@ import lombok.experimental.FieldDefaults;
 class SelectPreparedSpellsPanel extends SelectSpellsPanel {
 	
 	private SelectPreparedSpellsPanel(DDCharacter chara, int level, DDCharacterClass dclass, Prepared prep) {
-		super(chara, level, dclass, prep.getSpellsPreparedPreviouslyForLevel(level), 0,
-				prep.getSpellsPreparedPreviouslyForLevel(level), true);
+		super(chara, level, dclass, prep.getSpellsPreparedPreviouslyForLevel(level),
+				prep.getSpellsPreparedPreviouslyForLevel(level));
 	}
 
 	public SelectPreparedSpellsPanel(DDCharacter chara, int level, DDCharacterClass dclass) {

+ 27 - 10
src/main/lombok/org/leumasjaffe/charsheet/view/magic/SelectSpellsPanel.java

@@ -14,6 +14,7 @@ import org.leumasjaffe.charsheet.model.DDCharacter;
 import org.leumasjaffe.charsheet.model.DDCharacterClass;
 import org.leumasjaffe.charsheet.model.magic.DDSpell;
 import org.leumasjaffe.charsheet.model.magic.DDSpellbook;
+import org.leumasjaffe.charsheet.model.observable.IntValue;
 import org.leumasjaffe.charsheet.util.AbilityHelper;
 import org.leumasjaffe.event.SelectTableRowPopupMenuListener;
 import org.leumasjaffe.format.StringHelper;
@@ -40,6 +41,8 @@ import javax.swing.JScrollPane;
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
 public class SelectSpellsPanel extends JPanel {
 	
+	private static final String NONE = "<none>";
+
 	@AllArgsConstructor
 	private static class SelectSpellModel extends AbstractTableModel {
 		/**
@@ -83,23 +86,26 @@ public class SelectSpellsPanel extends JPanel {
 	
 	@Getter Collection<DDSpell> prepared;
 	boolean allowsDuplicates;
+	IntValue sharedValue;
 	SelectSpellModel modelPrepared, modelKnown;
 	
 	public SelectSpellsPanel(Info info, int level, Collection<DDSpell> prepared, int toPrepare,
-			Collection<DDSpell> avail, boolean allowsDuplicates) {
-		this(info.chara, level, info.dclass, prepared, toPrepare, avail, allowsDuplicates);
+			Collection<DDSpell> avail, boolean allowsDuplicates, IntValue val) {
+		this(info.chara, level, info.dclass, prepared, toPrepare, avail, allowsDuplicates, val);
 	}
 
 	public SelectSpellsPanel(DDCharacter chara, int level, 
 			DDCharacterClass dclass, Collection<DDSpell> prepared, int toPrepare,
-			Collection<DDSpell> avail, boolean allowsDuplicates) {
+			Collection<DDSpell> avail, boolean allowsDuplicates, IntValue sharedValue) {
 		this.allowsDuplicates = allowsDuplicates;
+		this.sharedValue = sharedValue;
 		putClientProperty(READY, true);
 		final DDSpellbook spellBook = dclass.getSpellBook().get();
 		this.prepared = new ArrayList<>(prepared);
 		final List<DDSpell> known = new ArrayList<>(avail);
 		this.modelPrepared = new SelectSpellModel(createPrepareModel(prepared, toPrepare));
 		this.modelKnown = new SelectSpellModel(known.stream().map(DDSpell::getName).toArray());
+		sharedValue.value(sharedValue.value() - this.modelPrepared.data.length + countNone());
 		
 		GridBagLayout gridBagLayout = new GridBagLayout();
 		gridBagLayout.columnWidths = new int[]{0, 40, 0, 0};
@@ -183,8 +189,9 @@ public class SelectSpellsPanel extends JPanel {
 		panelDivider.add(button, gbc_button);
 		button.addActionListener(e -> {
 			final int row = tablePrepared.getSelectedRow();
-			if (row != -1) {
-				modelPrepared.setValueAt("<none>", row, 0);
+			if (row != -1 && !modelPrepared.data[row].equals(NONE)) {
+				sharedValue.value(sharedValue.value() + 1);
+				modelPrepared.setValueAt(NONE, row, 0);
 			}
 			tablePrepared.getSelectionModel().clearSelection();
 			tablePrepared.repaint();
@@ -204,10 +211,14 @@ public class SelectSpellsPanel extends JPanel {
 		button_1.addActionListener(e -> {
 			final int[] rows = tableKnown.getSelectedRows();
 			final int[] orows = tablePrepared.getSelectedRows();
-			if (orows.length >= rows.length) {
+			if (sharedValue.value() == 0) {
+				JOptionPane.showMessageDialog(this, "You have exceeded the shared limit on new spells", 
+						"Error", JOptionPane.ERROR_MESSAGE);
+			} else if (orows.length >= rows.length) {
 				for (int i = 0; i < rows.length; ++i) {
 					if (wouldHaveIllegalDuplicate(rows[i])) continue;
 					modelPrepared.data[orows[i]] = modelKnown.data[rows[i]];
+					sharedValue.value(sharedValue.value() - 1);
 				}
 			} else if (orows.length == 0 && countNone() >= rows.length) {
 				replace(rows);
@@ -220,7 +231,7 @@ public class SelectSpellsPanel extends JPanel {
 			tablePrepared.getSelectionModel().clearSelection();
 			tablePrepared.repaint();
 			
-			if (!(Boolean) getClientProperty(READY) && !Arrays.asList(modelPrepared.data).contains("<none>")) {
+			if (!(Boolean) getClientProperty(READY) && !Arrays.asList(modelPrepared.data).contains(NONE)) {
 				this.prepared.clear();
 				for (Object o : modelPrepared.data) {
 					this.prepared.add(DDSpell.fromString((String) o)); // TODO
@@ -230,6 +241,11 @@ public class SelectSpellsPanel extends JPanel {
 		});
 	}
 
+	public SelectSpellsPanel(DDCharacter chara, int level, DDCharacterClass dclass,
+			Collection<DDSpell> prepared, Collection<DDSpell> avail) {
+		this(chara, level, dclass, prepared, 0, avail, true, new IntValue(-1));
+	}
+
 	private boolean wouldHaveIllegalDuplicate(int row) {
 		return !this.allowsDuplicates && Arrays.asList(modelPrepared.data).contains(modelKnown.data[row]);
 	}
@@ -244,7 +260,7 @@ public class SelectSpellsPanel extends JPanel {
 				data[i++] = sp.getName();
 			}
 			for (; i < toPrepare; ++i) {
-				data[i] = "<none>";
+				data[i] = NONE;
 			}
 			return data;
 		}
@@ -254,8 +270,9 @@ public class SelectSpellsPanel extends JPanel {
 		for (int i = 0; i < rows.length; ++i) {
 			if (wouldHaveIllegalDuplicate(i)) continue;
 			for (int j = 0; j < modelPrepared.data.length; ++j) {
-				if (!modelPrepared.data[j].equals("<none>")) continue;
+				if (!modelPrepared.data[j].equals(NONE)) continue;
 				modelPrepared.data[j] = modelKnown.data[i];
+				sharedValue.value(sharedValue.value() - 1);
 				break;
 			}
 		}
@@ -264,7 +281,7 @@ public class SelectSpellsPanel extends JPanel {
 	private int countNone() {
 		int cnt = 0;
 		for (Object o : modelPrepared.data) {
-			if (o.equals("<none>")) ++cnt;
+			if (o.equals(NONE)) ++cnt;
 		}
 		return cnt;
 	}