package org.leumasjaffe.charsheet.view.level; import static org.leumasjaffe.charsheet.view.level.LevelUpSpellPanel.SpellPickType.*; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.SortedSet; import java.util.TreeSet; import java.util.function.Consumer; import javax.swing.JPanel; import org.jdesktop.swingx.VerticalLayout; 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.BoolGate; import org.leumasjaffe.charsheet.model.observable.IntValue; import org.leumasjaffe.charsheet.view.magic.SelectSpellsPanel; import org.leumasjaffe.charsheet.view.magic.SelectSpellsPanel.Info; import org.leumasjaffe.charsheet.view.skills.SkillLevelUpPanel; import org.leumasjaffe.function.VoidVoidFunction; import org.leumasjaffe.observer.ObservableListener; import org.leumasjaffe.observer.ObserverDispatch; import lombok.AccessLevel; import lombok.experimental.FieldDefaults; import lombok.experimental.NonFinal; import java.awt.GridBagLayout; import javax.swing.JTabbedPane; import java.awt.GridBagConstraints; import java.awt.Insets; import java.awt.Component; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JLabel; @SuppressWarnings("serial") @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true) class UpdateClassWithLevelPanel extends JPanel { static int CHOOSE_SKILL_INDEX = 0, LEARN_SPELL_INDEX = 1, PREPARE_SPELL_INDEX = 2; LevelUpClassInfo levelUpInfo; JTabbedPane tabbedPane; SkillLevelUpPanel skills; @NonFinal Optional learnSpells = Optional.empty(); @NonFinal Optional prepSpells = Optional.empty(); BoolGate readyCount = new BoolGate(3); ObservableListener, BoolGate> listener; @NonFinal ObservableListener learnAndPrepareListener = null; public UpdateClassWithLevelPanel(LevelUpClassInfo info, VoidVoidFunction back, Consumer setReady) { this.levelUpInfo = info; info.ddClass.getLevel().value(info.toLevel); GridBagLayout gridBagLayout = new GridBagLayout(); gridBagLayout.columnWidths = new int[]{0, 0}; gridBagLayout.rowHeights = new int[]{0, 0, 0}; gridBagLayout.columnWeights = new double[]{1.0, Double.MIN_VALUE}; gridBagLayout.rowWeights = new double[]{1.0, 0.0, Double.MIN_VALUE}; setLayout(gridBagLayout); tabbedPane = new JTabbedPane(JTabbedPane.TOP); GridBagConstraints gbc_tabbedPane = new GridBagConstraints(); gbc_tabbedPane.insets = new Insets(0, 0, 5, 0); gbc_tabbedPane.fill = GridBagConstraints.BOTH; gbc_tabbedPane.gridx = 0; gbc_tabbedPane.gridy = 0; add(tabbedPane, gbc_tabbedPane); JPanel features = new JPanel(new VerticalLayout(2)); info.ddClass.getProto().getFeatures(info.toLevel).forEach(prop -> { features.add(new JLabel(prop.getName())); }); tabbedPane.addTab("Features", null, features, null); skills = new SkillLevelUpPanel(info.ddCharacter, info.ddClass) { @Override public void setIsReady(boolean b) { readyCount.set(CHOOSE_SKILL_INDEX, b); ObserverDispatch.notifySubscribers(readyCount); } }; tabbedPane.addTab("Skills", null, skills, null); info.ddClass.getSpellBook().ifPresent(sb -> { readyCount.set(LEARN_SPELL_INDEX, !sb.learnsSpells()); readyCount.set(PREPARE_SPELL_INDEX, !sb.preparesSpells()); if (sb.learnsSpells()) { createPanelsForLearnSpell(sb); } else if (sb.preparesSpells()) { createPanelForPrepareSpells(); } }); JPanel panel = new JPanel(); GridBagConstraints gbc_panel = new GridBagConstraints(); gbc_panel.fill = GridBagConstraints.BOTH; gbc_panel.gridx = 0; gbc_panel.gridy = 1; add(panel, gbc_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); JButton btnBack = new JButton("Back"); GridBagConstraints gbc_btnBack = new GridBagConstraints(); gbc_btnBack.insets = new Insets(0, 0, 0, 5); gbc_btnBack.gridx = 0; gbc_btnBack.gridy = 0; panel.add(btnBack, gbc_btnBack); btnBack.addActionListener(e -> back.apply()); Component horizontalGlue = Box.createHorizontalGlue(); GridBagConstraints gbc_horizontalGlue = new GridBagConstraints(); gbc_horizontalGlue.gridx = 1; gbc_horizontalGlue.gridy = 0; panel.add(horizontalGlue, gbc_horizontalGlue); listener = new ObservableListener<>(setReady, (c, v) -> { c.accept(v.all()); }); listener.setObserved(readyCount); } private void createPanelForPrepareSpells() { prepSpells = Optional.of(new LevelUpSpellPanel(PREPARE, new SelectSpellsPanel.Info(levelUpInfo.ddCharacter, levelUpInfo.ddClass), readyCount.handle(PREPARE_SPELL_INDEX))); tabbedPane.addTab("Prepare Spells", null, prepSpells.get(), null); } private void createPanelsForLearnSpell(DDSpellbook sb) { learnSpells = Optional.of(new LevelUpSpellPanel(LEARN, new SelectSpellsPanel.Info(levelUpInfo.ddCharacter, levelUpInfo.ddClass), readyCount.handle(LEARN_SPELL_INDEX))); tabbedPane.addTab("Learn Spells", null, learnSpells.get(), null); if (sb.preparesSpells()) { learnAndPrepareListener = new ObservableListener<>(this, (c, v) -> { if (v.get(LEARN_SPELL_INDEX)) { if (!prepSpells.isPresent()) c.createPrepareLearnedSpellPanel(learnSpells.get()); } else { prepSpells.ifPresent(c.tabbedPane::remove); c.prepSpells = Optional.empty(); } }); learnAndPrepareListener.setObserved(readyCount); } } private void createPrepareLearnedSpellPanel(LevelUpSpellPanel spells) { LevelUpSpellPanel.SpellPicker pick = new LevelUpSpellPanel.SpellPicker() { @Override public List> getSpellCounts(Info info) { return PREPARE.getSpellCounts(info); } @Override public Collection getAvailableSpells(Info info, int i) { final Collection start = new ArrayList<>(PREPARE.getAvailableSpells(info, i)); if (spells.getPanels().get(i) != null) { start.addAll(spells.getPanels().get(i).getPrepared()); } return start; } }; prepSpells = Optional.of(new LevelUpSpellPanel(pick, new SelectSpellsPanel.Info(levelUpInfo.ddCharacter, levelUpInfo.ddClass), readyCount.handle(PREPARE_SPELL_INDEX))); tabbedPane.addTab("Prepare Spells", null, prepSpells.get(), null); } @Override public void removeNotify() { super.removeNotify(); ObserverDispatch.unsubscribeAll(listener); ObserverDispatch.unsubscribeAll(learnAndPrepareListener); } private void commitSpellbook(DDSpellbook book) { learnSpells.ifPresent(pan -> { final List selections = pan.getPanels(); for (int i = 0; i < selections.size(); ++i) { if (selections.get(i) == null) continue; List known = new ArrayList<>(book.spellsKnownAtLevel(i)); known.addAll(selections.get(i).getPrepared()); book.learnSpells(i, known); } }); prepSpells.ifPresent(pan -> { final List selections = pan.getPanels(); for (int i = 0; i < selections.size(); ++i) { if (selections.get(i) == null) continue; List known = new ArrayList<>(book.spellsPreparedAtLevel(i)); known.addAll(selections.get(i).getPrepared()); book.prepareSpells(i, known); } }); } public void commitAllChanges() { final String className = levelUpInfo.ddClass.getName(); skills.commitAllChanges(); final Optional maybeBook = levelUpInfo.ddClass.getSpellBook(); maybeBook.ifPresent(this::commitSpellbook); final SortedSet classes = new TreeSet<>(levelUpInfo.ddCharacter.getClasses()); classes.removeIf(cc -> cc.getName().equals(className)); classes.add(levelUpInfo.ddClass); levelUpInfo.ddCharacter.setClasses(classes); // TODO: Acquire features final IntValue exp = levelUpInfo.ddCharacter.getExperience(); final int neededExp = ExperienceDialog.experienceForLevel(levelUpInfo.ddCharacter.getLevel()); if (exp.value() < neededExp) { exp.value(neededExp); } ObserverDispatch.notifySubscribers(exp); ObserverDispatch.notifySubscribers(levelUpInfo.ddCharacter); } }