public class CharacterItemModel {

  private DescriptiveFeatures descriptiveFeatures;
  private Item item;
  private final Announcer<ChangeListener> featuresChangeAnnouncer =
      Announcer.to(ChangeListener.class);

  public CharacterItemModel(DescriptiveFeatures descriptiveFeatures) {
    this.descriptiveFeatures = descriptiveFeatures;
  }

  public CharacterItemModel(CharacterIdentifier identifier, Item item) {
    setItem(identifier, item);
  }

  public DescriptiveFeatures getDescriptiveFeatures() {
    return descriptiveFeatures;
  }

  public void setItem(Item item) {
    CharacterIdentifier identifier = descriptiveFeatures.getIdentifier();
    setItem(identifier, item);
  }

  private void setItem(CharacterIdentifier identifier, Item item) {
    this.item = item;
    this.descriptiveFeatures = new LoadedDescriptiveFeatures(identifier, item);
    Hero hero = (Hero) item.getItemData();
    HeroConceptFetcher.fetch(hero).getCaste().addChangeListener(new AnnouncingChangeListener());
    HeroDescriptionFetcher.fetch(hero)
        .getName()
        .addTextChangedListener(
            new ObjectValueListener<String>() {
              @Override
              public void valueChanged(String newValue) {
                featuresChangeAnnouncer.announce().changeOccurred();
              }
            });
    item.getItemData().getChangeManagement().addDirtyListener(new AnnouncingChangeListener());
  }

  public boolean isLoaded() {
    return item != null;
  }

  public Item getItem() {
    return item;
  }

  public void whenFeaturesChange(ChangeListener listener) {
    featuresChangeAnnouncer.addListener(listener);
  }

  private class AnnouncingChangeListener implements ChangeListener {
    @Override
    public void changeOccurred() {
      featuresChangeAnnouncer.announce().changeOccurred();
    }
  }
}
public class CharacterListening implements ICharacterListening {

  private final Announcer<ICharacterChangeListener> changeControl =
      Announcer.to(ICharacterChangeListener.class);
  private final CharacterListenerMapping listenerMapping =
      new CharacterListenerMapping(changeControl);

  @Override
  public void addChangeListener(ICharacterChangeListener changeListener) {
    changeControl.addListener(changeListener);
  }

  public void removeTraitListening(IDefaultTrait trait) {
    listenerMapping.removeTraitListening(trait);
  }

  public void addTraitListening(ITrait trait) {
    listenerMapping.addTraitListening(trait);
  }

  public void fireCharacterChanged() {
    changeControl.announce().characterChanged();
  }

  public void fireCasteChanged() {
    changeControl.announce().casteChanged();
  }

  public void fireExperiencedChanged(boolean isExperienced) {
    changeControl.announce().experiencedChanged(isExperienced);
  }
}
public class MusicDatabasePersister {

  private final LibraryPersister libaryPersister = new LibraryPersister();
  private final SelectionPersister selectionPersister = new SelectionPersister();
  private final SearchPersister searchPersister = new SearchPersister();
  private final ObjectContainer db;
  private final Announcer<ITrackDeletionListener> listeners =
      Announcer.to(ITrackDeletionListener.class);

  public MusicDatabasePersister(ObjectContainer container) {
    db = container;
    libaryPersister.addTrackDeletionListener(
        new ITrackDeletionListener() {
          @Override
          public void trackRemoved(DbMp3Track track) {
            fireTrackDeleted(track);
          }
        });
  }

  public void pruneSelections(DbMp3Track track) {
    DbMusicSelection[] allSelections = selectionPersister.getAllSelections(db);
    for (DbMusicSelection selection : allSelections) {
      selection.removeTracks(new IMp3Track[] {track});
      selectionPersister.updateSelection(db, selection);
    }
    db.commit();
  }

  private void fireTrackDeleted(DbMp3Track track) {
    listeners.announce().trackRemoved(track);
  }

  public void addTrackDeletionListener(ITrackDeletionListener listener) {
    listeners.addListener(listener);
  }

  public void addLibrary(String libraryName) {
    libaryPersister.addLibrary(db, libraryName);
    db.commit();
  }

  public ILibrary[] getAllLibraries() {
    return libaryPersister.getAllLibraries(db);
  }

  public boolean isRegisteredLibrary(String name) {
    return libaryPersister.isRegisteredLibrary(db, name);
  }

  public IMp3Track[] getTracksFromLibrary(String name) {
    DbLibrary library = libaryPersister.getLibrary(db, name);
    if (library == null) {
      return new IMp3Track[0];
    }
    return library.getMp3Items();
  }

  public void addTracks(String name, List<IMp3Track> tracks) {
    libaryPersister.addTracks(db, name, tracks);
    db.commit();
  }

  public void updateLibaryName(ILibrary library, String newName) {
    libaryPersister.updateLibraryName(db, library, newName);
    db.commit();
  }

  public void removeLibrary(String name) {
    libaryPersister.removeLibrary(db, name);
    db.commit();
  }

  public void removeTracksFromLibrary(String name, IMp3Track[] tracks) {
    for (IMp3Track track : tracks) {
      libaryPersister.removeTrackFromLibrary(db, name, track);
    }
    db.commit();
  }

  public void addSelection(String string) {
    selectionPersister.updateSelection(db, new DbMusicSelection(string, db));
    db.commit();
  }

  public IMusicSelection[] getAllSelections() {
    return selectionPersister.getAllSelections(db);
  }

  public DbMusicSelection getSelectionByName(String name) {
    return selectionPersister.getSelectionByName(db, name);
  }

  public void removeSelection(IMusicSelection selection) {
    if (selection instanceof DbMusicSelection) {
      selectionPersister.removeSelection(db, (DbMusicSelection) selection);
    } else {
      throw new IllegalArgumentException(
          "Selection must be of type DbMusicSelection"); //$NON-NLS-1$
    }
    db.commit();
  }

  public void updateTrackInfo(IMp3Track selectedTrack) {
    if (selectedTrack instanceof DbMp3Track) {
      selectionPersister.updateTrackInfo(db, (DbMp3Track) selectedTrack);
    } else {
      throw new IllegalArgumentException("Track must be of type DbMp3Track"); // $NON-NLS-1$
    }
    db.commit();
  }

  public IMp3Track[] executeSearch(IExtendedSearchParameter[] parameters) {
    return searchPersister.executeSearch(db, parameters);
  }

  public <C> List<C> getAllObjects(Class<C> componentType) {
    return db.query(componentType);
  }

  public <C> void setSimpleObject(C object) {
    db.set(object);
  }

  public void updateSelectionName(IMusicSelection selection, String newName) {
    DbMusicSelection dbSelection = (DbMusicSelection) selection;
    dbSelection.setName(newName);
    selectionPersister.updateSelection(db, dbSelection);
    db.commit();
  }

  public void updateSelection(IMusicSelection selection) {
    selectionPersister.updateSelection(db, (DbMusicSelection) selection);
  }
}
Beispiel #4
0
public class SpecialtyView extends AbstractTraitView implements ISpecialtyView {
  private final Announcer<IChangeListener> control = Announcer.to(IChangeListener.class);
  private final Component abilityLabel;
  private final Component separatorLabel = new JLabel("-"); // $NON-NLS-1$
  private final Component specialtyLabel;
  private JButton deleteButton;
  private JPanel traitPanel;
  private final Icon deleteIcon;

  public SpecialtyView(
      IntegerViewFactory configuration,
      String labelText,
      Icon deleteIcon,
      String id,
      int value,
      int maxValue) {
    super(configuration, labelText, value, maxValue, null);
    this.deleteIcon = deleteIcon;
    specialtyLabel = new JLabel(id);
    abilityLabel = new JLabel(labelText);
  }

  @SuppressWarnings("serial")
  public void addComponents(JPanel panel) {
    this.traitPanel = panel;
    panel.add(abilityLabel);
    panel.add(separatorLabel);
    panel.add(specialtyLabel, new CC().pushX().growX());
    panel.add(getValueDisplay().getComponent());
    deleteButton =
        new JButton(
            new AbstractAction(null, deleteIcon) {
              @Override
              public void actionPerformed(ActionEvent e) {
                fireDeletionPerformed();
              }
            });
    deleteButton.setPreferredSize(
        new Dimension(deleteIcon.getIconWidth() + 4, deleteIcon.getIconHeight() + 4));
    panel.add(deleteButton, LayoutUtils.constraintsForImageButton(deleteButton));
  }

  @Override
  public void addDeleteListener(IChangeListener listener) {
    control.addListener(listener);
  }

  private void fireDeletionPerformed() {
    control.announce().changeOccurred();
  }

  @Override
  public void delete() {
    traitPanel.remove(abilityLabel);
    traitPanel.remove(separatorLabel);
    traitPanel.remove(specialtyLabel);
    traitPanel.remove(getValueDisplay().getComponent());
    traitPanel.remove(deleteButton);
    traitPanel.revalidate(); // Remove this line to keep the positions of specialties constant.
    traitPanel.repaint();
  }

  @Override
  public void setDeleteButtonEnabled(boolean enabled) {
    deleteButton.setEnabled(enabled);
  }
}
Beispiel #5
0
public class UpdateChecker implements IUpdateChecker {

  public static final String Update_Url = "http://anathema.github.com/version/version.txt";
  private final IResources resources;
  private final Announcer<IChangeListener> control = Announcer.to(IChangeListener.class);
  private IMessageData data;
  private String remoteVersion;
  private Boolean success = null;

  public UpdateChecker(IResources resources) {
    this.resources = resources;
  }

  public void checkForUpdates() {
    try {
      String response = getVersionData();
      String[] split = response.split("#"); // $NON-NLS-1$
      boolean newVersionAvailable = remoteIsNewer(split[1]);
      this.remoteVersion = split[0];
      if (newVersionAvailable) {
        this.success = true;
        this.data =
            new MessageData("Help.UpdateCheck.Outdated", MessageType.INFORMATION); // $NON-NLS-1$
      } else {
        this.success = true;
        this.data =
            new MessageData("Help.UpdateCheck.UpToDate", MessageType.INFORMATION); // $NON-NLS-1$
      }
    } catch (IOException e) {
      this.success = false;
      this.data = new MessageData("Help.UpdateCheck.IOException", MessageType.ERROR); // $NON-NLS-1$
    } catch (Exception e) {
      this.success = false;
      this.data =
          new MessageData(
              "Help.UpdateCheck.GeneralException", MessageType.ERROR); // $NON-NLS-1$
    }
    control.announce().changeOccurred();
  }

  @Override
  public Boolean isCheckSuccessful() {
    return success;
  }

  @Override
  public String getCurrentVersion() {
    return resources.getString("Anathema.Version.Numeric"); // $NON-NLS-1$
  }

  @Override
  public String getLatestVersion() {
    return remoteVersion;
  }

  @Override
  public IMessageData getMessageData() {
    return data;
  }

  private boolean remoteIsNewer(String string) throws ParseException {
    DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, Locale.ENGLISH);
    Date currentVersionDate =
        dateFormat.parse(resources.getString("Anathema.Version.Built")); // $NON-NLS-1$
    Date remoteVersionDate = dateFormat.parse(string);
    return currentVersionDate.compareTo(remoteVersionDate) < 0;
  }

  private String getVersionData() throws IOException {
    URL url = new URL(Update_Url); // $NON-NLS-1$
    BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
    String response = in.readLine();
    in.close();
    return response;
  }

  @Override
  public void addDataChangedListener(IChangeListener listener) {
    control.addListener(listener);
  }
}
Beispiel #6
0
public class LibraryControl implements ILibraryControl {

  private final Announcer<ILibraryChangedListener> listenerControl =
      Announcer.to(ILibraryChangedListener.class);
  private MusicDatabasePersister musicDataBasePersister;

  public LibraryControl(MusicDatabasePersister persister) {
    this.musicDataBasePersister = persister;
  }

  @Override
  public void updateLibrary(ILibrary library, String newName) {
    musicDataBasePersister.updateLibaryName(library, newName);
    fireLibraryChanged(newName);
  }

  @Override
  public ILibrary[] getAllLibraries() {
    return musicDataBasePersister.getAllLibraries();
  }

  @Override
  public boolean containsLibraryName(String name) {
    return musicDataBasePersister.isRegisteredLibrary(name);
  }

  @Override
  public void addNewLibrary(String name) {
    musicDataBasePersister.addLibrary(name);
    fireLibraryChanged(name);
  }

  @Override
  public void addNewUnnamedLibrary(String unnamedLibraryBaseString) {
    int count = 1;
    ILibrary[] allLibraries = getAllLibraries();
    ILibrary library =
        getLibraryByName(allLibraries, createLibrarayName(unnamedLibraryBaseString, count));
    while (library != null) {
      count++;
      library = getLibraryByName(allLibraries, createLibrarayName(unnamedLibraryBaseString, count));
    }
    String libraryName = createLibrarayName(unnamedLibraryBaseString, count);
    addNewLibrary(libraryName);
  }

  private String createLibrarayName(String unnamedLibraryBaseString, int count) {
    return unnamedLibraryBaseString + " " + count; // $NON-NLS-1$
  }

  private void fireLibraryChanged(String name) {
    ILibrary[] allLibraries = getAllLibraries();
    ILibrary newLibrary = getLibraryByName(allLibraries, name);
    listenerControl.announce().librariesChanged(allLibraries, newLibrary);
  }

  protected ILibrary getLibraryByName(ILibrary[] allLibraries, String name) {
    for (ILibrary library : allLibraries) {
      if (library.getName().equals(name)) {
        return library;
      }
    }
    return null;
  }

  @Override
  public void addLibraryChangedListener(ILibraryChangedListener listener) {
    listenerControl.addListener(listener);
  }

  @Override
  public void addTracks(String name, List<IMp3Track> tracks) {
    musicDataBasePersister.addTracks(name, tracks);
  }

  @Override
  public IMusicFolderWalker createMusicFolderWalker(Path folder) throws IOException {
    return new MusicFolderWalker(folder);
  }

  @Override
  public void removeLibrary(ILibrary library) {
    musicDataBasePersister.removeLibrary(library.getName());
    fireLibraryChanged(null);
  }

  @Override
  public void removeTracksFromLibrary(ILibrary library, IMp3Track[] tracks) {
    musicDataBasePersister.removeTracksFromLibrary(library.getName(), tracks);
    fireLibraryChanged(library.getName());
  }

  @Override
  public void addTrack(String name, Path mp3File) throws AnathemaException, IOException {
    List<IMp3Track> tracks = new ArrayList<>();
    tracks.add(new FileMp3Track(mp3File));
    musicDataBasePersister.addTracks(name, tracks);
  }
}
Beispiel #7
0
public class CharmConfiguration implements ICharmConfiguration {

  private final ISpecialCharmManager manager;
  private final MartialArtsCharmTree martialArtsCharmTree;
  private final Map<Identified, ICharmTree> alienTreesByType =
      new HashMap<Identified, ICharmTree>();
  private final Map<Identified, ILearningCharmGroup[]> nonMartialArtsGroupsByType =
      new HashMap<Identified, ILearningCharmGroup[]>();
  private final Map<ICharacterType, ICharmTemplate> templatesByType =
      new HashMap<ICharacterType, ICharmTemplate>();
  private final ICharacterType[] types;
  private final ILearningCharmGroupContainer learningCharmGroupContainer =
      new ILearningCharmGroupContainer() {
        @Override
        public ILearningCharmGroup getLearningCharmGroup(ICharm charm) {
          return getGroup(charm);
        }
      };
  private ILearningCharmGroup[] martialArtsGroups;
  private final ICharacterModelContext context;
  private final Announcer<IChangeListener> control = Announcer.to(IChangeListener.class);
  private final ICharmProvider provider;
  private final ICharmGroupArbitrator arbitrator;
  private List<ICharmFilter> filterSet = new ArrayList<ICharmFilter>();
  private PrerequisiteModifyingCharms prerequisiteModifyingCharms;

  public CharmConfiguration(
      IHealthConfiguration health,
      ICharacterModelContext context,
      ITemplateRegistry registry,
      ICharmProvider provider) {
    this.manager = new SpecialCharmManager(this, health, context);
    this.context = context;
    this.provider = provider;
    List<ICharacterType> allCharacterTypes = new ArrayList<ICharacterType>();
    ICharmTemplate nativeCharmTemplate = getNativeCharmTemplate(registry);
    this.arbitrator = new LearningCharmGroupArbitrator(nativeCharmTemplate, context);
    this.martialArtsCharmTree = new MartialArtsCharmTree(nativeCharmTemplate);
    this.martialArtsGroups = createGroups(martialArtsCharmTree.getAllCharmGroups());
    initCharacterType(nativeCharmTemplate, getNativeCharacterType());
    allCharacterTypes.add(getNativeCharacterType());
    initAlienTypes(registry, allCharacterTypes);
    initUniqueTypes(nativeCharmTemplate);
    initSpecialCharmConfigurations();
    types = allCharacterTypes.toArray(new ICharacterType[allCharacterTypes.size()]);
    filterSet.add(new ObtainableCharmFilter(this));
    filterSet.add(new CharacterSourceBookFilter(this));
    filterSet.add(new EssenceLevelCharmFilter());
  }

  private ICharmTemplate getNativeCharmTemplate(ITemplateRegistry registry) {
    IBasicCharacterData basicCharacterContext = context.getBasicCharacterContext();
    ITemplateType templateType = basicCharacterContext.getTemplateType();
    ICharacterTemplate template = registry.getTemplate(templateType);
    IMagicTemplate magicTemplate = template.getMagicTemplate();
    return magicTemplate.getCharmTemplate();
  }

  @Override
  public void addCharmLearnListener(ICharmLearnListener listener) {
    for (ILearningCharmGroup group : getAllGroups()) {
      group.addCharmLearnListener(listener);
    }
  }

  @Override
  public ICharmIdMap getCharmIdMap() {
    List<ICharmIdMap> trees = new ArrayList<ICharmIdMap>(alienTreesByType.values());
    trees.add(martialArtsCharmTree);
    ICharmIdMap[] treeArray = trees.toArray(new ICharmIdMap[trees.size()]);
    return new GroupedCharmIdMap(treeArray);
  }

  @Override
  public ISpecialCharm[] getSpecialCharms() {
    return provider.getSpecialCharms(
        new MartialArtsLearnableArbitrator(martialArtsCharmTree),
        getCharmIdMap(),
        getNativeCharacterType());
  }

  private void initSpecialCharmConfigurations() {
    ICharmIdMap charmIdMap = getCharmIdMap();
    ISpecialCharm[] specialCharms = getSpecialCharms();
    for (ISpecialCharm specialCharm : specialCharms) {
      ICharm charm = charmIdMap.getCharmById(specialCharm.getCharmId());
      if (charm == null) {
        continue;
      }
      ILearningCharmGroup group = getGroupById(charm.getCharacterType(), charm.getGroupId());
      manager.registerSpecialCharmConfiguration(specialCharm, charm, group);
    }
  }

  private ILearningCharmGroup[] createGroups(ICharmGroup[] charmGroups) {
    List<ILearningCharmGroup> newGroups = new ArrayList<ILearningCharmGroup>();
    ICharmLearnListener mergedListener =
        new CharmLearnAdapter() {
          @Override
          public void charmLearned(ICharm charm) {
            for (ICharm mergedCharm : charm.getMergedCharms()) {
              if (!isLearned(mergedCharm)
                  && isLearnableWithoutPrerequisites(mergedCharm)
                  && CharmConfiguration.this.getSpecialCharmConfiguration(mergedCharm) == null) {
                getGroup(mergedCharm).learnCharm(mergedCharm, isExperienced());
              }
            }

            for (ICharm child : charm.getLearnChildCharms()) {
              boolean learnedMerged = false;
              for (ICharm mergedCharm : child.getMergedCharms()) {
                learnedMerged = learnedMerged || isLearned(mergedCharm);
              }
              if (learnedMerged && isLearnable(child)) {
                getGroup(child).learnCharm(child, isExperienced());
              }
            }
          }

          @Override
          public void charmForgotten(ICharm charm) {
            boolean forgetMerges = true;
            for (ICharm parentCharm : charm.getParentCharms()) {
              forgetMerges = forgetMerges && isLearned(parentCharm);
            }
            if (forgetMerges) {
              for (ICharm mergedCharm : charm.getMergedCharms()) {
                if (isLearned(mergedCharm) && isUnlearnableWithoutConsequences(mergedCharm)) {
                  getGroup(mergedCharm).forgetCharm(mergedCharm, isExperienced());
                }
              }
            }
          }

          @Override
          public void recalculateRequested() {
            for (ICharm charm : getLearnedCharms(true)) {
              boolean prereqsMet = true;
              for (ICharm parent : charm.getParentCharms()) {
                for (String subeffectRequirement : charm.getParentSubeffects()) {
                  if (getSubeffectParent(subeffectRequirement).equals(parent.getId())) {
                    ISpecialCharmConfiguration config =
                        getSpecialCharmConfiguration(getSubeffectParent(subeffectRequirement));
                    if (config instanceof IMultipleEffectCharmConfiguration) {
                      IMultipleEffectCharmConfiguration mConfig =
                          (IMultipleEffectCharmConfiguration) config;
                      prereqsMet =
                          prereqsMet
                              && mConfig
                                  .getEffectById(getSubeffect(subeffectRequirement))
                                  .isLearned();
                    }
                    if (config instanceof IMultiLearnableCharmConfiguration) {
                      IMultiLearnableCharmConfiguration mConfig =
                          (IMultiLearnableCharmConfiguration) config;
                      String effect = getSubeffect(subeffectRequirement);
                      int requiredCount = Integer.parseInt(effect.replace("Repurchase", ""));
                      prereqsMet = mConfig.getCurrentLearnCount() >= requiredCount;
                    }
                  }
                }
              }
              if (!prereqsMet) {
                getGroup(charm).forgetCharm(charm, isExperienced());
              }
            }
          }
        };
    for (ICharmGroup charmGroup : charmGroups) {
      ILearningCharmGroup group =
          new LearningCharmGroup(
              getLearnStrategy(), charmGroup, this, learningCharmGroupContainer, this);
      newGroups.add(group);

      group.addCharmLearnListener(mergedListener);
    }
    return newGroups.toArray(new ILearningCharmGroup[newGroups.size()]);
  }

  private String getSubeffectParent(String subeffect) {
    return subeffect.split("\\.")[0] + "." + subeffect.split("\\.")[1];
  }

  private String getSubeffect(String subeffect) {
    return subeffect.split("\\.")[3];
  }

  @Override
  public ILearningCharmGroup[] getAllGroups() {
    List<ILearningCharmGroup> allGroups = new ArrayList<ILearningCharmGroup>();
    for (ILearningCharmGroup[] groups : nonMartialArtsGroupsByType.values()) {
      allGroups.addAll(Arrays.asList(groups));
    }
    allGroups.addAll(Arrays.asList(martialArtsGroups));
    return allGroups.toArray(new ILearningCharmGroup[allGroups.size()]);
  }

  private ICharmTemplate getCharmTemplate(ICharacterType type) {
    return templatesByType.get(type);
  }

  private ICharacterType getCharacterType(String charmId) {
    String characterType = charmId.substring(0, charmId.indexOf(".")); // $NON-NLS-1$
    for (ICharacterType type : types) {
      if (type.getId().equals(characterType)) {
        return type;
      }
    }
    throw new IllegalArgumentException(
        "Charm Id did not contain a valid character type"); //$NON-NLS-1$
  }

  @Override
  public String getCharmTrueName(String charmId) {
    return provider.getCharmRename(charmId);
  }

  @Override
  public ICharm getCharmById(String charmId) {
    charmId = getCharmTrueName(charmId);
    ICharm charm = martialArtsCharmTree.getCharmById(charmId);
    if (charm != null) {
      return charm;
    }
    ICharacterType characterType = getCharacterType(charmId);
    charm = getCharmTree(characterType).getCharmById(charmId);
    if (charm != null) {
      return charm;
    }
    ICharmTemplate charmTemplate = templatesByType.get(getNativeCharacterType());
    if (charmTemplate.hasUniqueCharms()) {
      IUniqueCharmType uniqueType = charmTemplate.getUniqueCharmType();
      charm = getCharmTree(uniqueType.getId()).getCharmById(charmId);
      if (charm != null) {
        return charm;
      }
    }
    throw new IllegalArgumentException(
        "No charm found for id \"" + charmId + "\""); // $NON-NLS-1$ //$NON-NLS-2$
  }

  private ICharmIdMap getCharmTree(Identified type) {
    return alienTreesByType.get(type);
  }

  @Override
  public ICharm[] getCreationLearnedCharms() {
    List<ICharm> allLearnedCharms = new ArrayList<ICharm>();
    for (ILearningCharmGroup group : getAllGroups()) {
      Collections.addAll(allLearnedCharms, group.getCreationLearnedCharms());
    }
    return allLearnedCharms.toArray(new ICharm[allLearnedCharms.size()]);
  }

  @Override
  public ILearningCharmGroup[] getCharmGroups(Identified type) {
    if (MartialArtsUtilities.MARTIAL_ARTS.equals(type)) {
      return martialArtsGroups;
    }
    return Functions.forMap(nonMartialArtsGroupsByType, new ILearningCharmGroup[0]).apply(type);
  }

  private ILearningCharmGroup[] getMartialArtsGroups() {
    return getCharmGroups(MartialArtsUtilities.MARTIAL_ARTS);
  }

  @Override
  public ICharm[] getLearnedCharms(boolean experienced) {
    List<ICharm> allLearnedCharms = new ArrayList<ICharm>();
    for (ILearningCharmGroup group : getAllGroups()) {
      Collections.addAll(allLearnedCharms, group.getCreationLearnedCharms());
      if (experienced) {
        Collections.addAll(allLearnedCharms, group.getExperienceLearnedCharms());
      }
    }
    return allLearnedCharms.toArray(new ICharm[allLearnedCharms.size()]);
  }

  @Override
  public ISpecialCharmConfiguration getSpecialCharmConfiguration(ICharm charm) {
    return manager.getSpecialCharmConfiguration(charm);
  }

  private void initCharacterType(ICharmTemplate charmTemplate, ICharacterType type) {
    CharmTree charmTree = new CharmTree(charmTemplate);
    alienTreesByType.put(type, charmTree);
    ILearningCharmGroup[] groups = createGroups(charmTree.getAllCharmGroups());
    nonMartialArtsGroupsByType.put(type, groups);
    templatesByType.put(type, charmTemplate);
  }

  private void initUniqueTypes(ICharmTemplate charmTemplate) {
    if (!charmTemplate.hasUniqueCharms()) {
      return;
    }
    IUniqueCharmType type = charmTemplate.getUniqueCharmType();
    CharmTree charmTree = new CharmTree(charmTemplate.getUniqueCharms());
    ILearningCharmGroup[] groups = createGroups(charmTree.getAllCharmGroups());
    nonMartialArtsGroupsByType.put(type.getId(), groups);
    alienTreesByType.put(type.getId(), charmTree);
  }

  private ICharmTemplate getCharmTemplate(ITemplateRegistry registry, ICharacterType type) {
    ICharacterTemplate defaultTemplate = registry.getDefaultTemplate(type);
    if (defaultTemplate == null) {
      return null;
    }
    return defaultTemplate.getMagicTemplate().getCharmTemplate();
  }

  private void initAlienTypes(ITemplateRegistry registry, List<ICharacterType> characterTypes) {
    for (ICharacterType type : CharacterType.values()) {
      if (characterTypes.contains(type)) {
        continue;
      }
      ICharmTemplate charmTemplate = getCharmTemplate(registry, type);
      if (charmTemplate != null && charmTemplate.canLearnCharms()) {
        initCharacterType(charmTemplate, type);
        characterTypes.add(type);
      }
    }
  }

  private ICharacterType getNativeCharacterType() {
    return context.getBasicCharacterContext().getCharacterType();
  }

  @Override
  public void unlearnAllAlienCharms() {
    for (ILearningCharmGroup[] groups : nonMartialArtsGroupsByType.values()) {
      for (ILearningCharmGroup group : groups) {
        if (group.getCharacterType() != getNativeCharacterType()) {
          group.forgetAll();
        }
      }
    }
    for (ILearningCharmGroup group : martialArtsGroups) {
      group.unlearnExclusives();
    }
  }

  @Override
  public ILearningCharmGroup getGroup(String characterTypeId, String groupName) {
    ICharacterType characterType =
        characterTypeId == null ? getNativeCharacterType() : CharacterType.getById(characterTypeId);
    return getGroupById(characterType, groupName);
  }

  @Override
  public ICharacterType[] getCharacterTypes(boolean includeAlienTypes) {
    if (!includeAlienTypes) {
      return new ICharacterType[] {getNativeCharacterType()};
    }
    return types;
  }

  public void initListening() {
    context
        .getCharacterListening()
        .addChangeListener(
            new GlobalCharacterChangeAdapter() {
              @Override
              public void characterChanged() {
                verifyCharms();
                fireLearnConditionsChanged();
              }
            });
    addCharmLearnListener(
        new CharmLearnAdapter() {
          @Override
          public void charmForgotten(ICharm charm) {
            fireLearnConditionsChanged();
          }

          @Override
          public void charmLearned(ICharm charm) {
            fireLearnConditionsChanged();
          }
        });
  }

  private void verifyCharms() {
    if (!context.isFullyLoaded()) {
      return;
    }
    List<ICharm> charmsToUnlearn = new ArrayList<ICharm>();
    for (ICharm charm : this.getLearnedCharms(true)) {
      boolean prerequisitesForCharmAreNoLongerMet = !isLearnable(charm);
      boolean charmCanBeUnlearned = isUnlearnable(charm);
      if (prerequisitesForCharmAreNoLongerMet && charmCanBeUnlearned) {
        charmsToUnlearn.add(charm);
      }
    }
    for (ICharm charm : charmsToUnlearn) {
      ILearningCharmGroup group = learningCharmGroupContainer.getLearningCharmGroup(charm);
      boolean learnedAtCreation = group.isLearned(charm, false);
      boolean learnedWithExperience = !learnedAtCreation;
      group.forgetCharm(charm, learnedWithExperience);
    }
  }

  @Override
  public void addLearnableListener(IChangeListener listener) {
    control.addListener(listener);
  }

  private void fireLearnConditionsChanged() {
    control.announce().changeOccurred();
  }

  private ICharmLearnStrategy getLearnStrategy() {
    return context.getCharmContext().getCharmLearnStrategy();
  }

  @Override
  public final boolean isLearnable(ICharm charm) {
    if (isAlienCharm(charm)) {
      ICasteType casteType = context.getBasicCharacterContext().getCasteType();
      if (!getCharmTemplateForCharacterType().isAllowedAlienCharms(casteType)) {
        return false;
      }
      if (charm.hasAttribute(ICharmData.NATIVE)) {
        return false;
      }
    }
    if (charm.isBlockedByAlternative(context.getMagicCollection())) {
      return false;
    }
    if (isMartialArtsCharm(charm)) {
      boolean isSiderealFormCharm = isFormCharm(charm) && hasLevel(Sidereal, charm);
      MartialArtsCharmConfiguration martialArtsConfiguration =
          new DefaultMartialArtsCharmConfiguration(
              this, context.getMagicCollection(), context.getBasicCharacterContext());
      if (isSiderealFormCharm && !martialArtsConfiguration.isAnyCelestialStyleCompleted()) {
        return false;
      }
      if (!getMartialArtsRulesForCharacterType()
          .isCharmAllowed(charm, martialArtsConfiguration, isExperienced())) {
        return false;
      }
    }
    ICharm[] learnedCharms = getLearnedCharms(true);
    for (IndirectCharmRequirement requirement : charm.getAttributeRequirements()) {
      if (!requirement.isFulfilled(learnedCharms)) {
        return false;
      }
    }
    if (!(new CharmTraitRequirementChecker(getPrerequisiteModifyingCharms(), context, this)
        .areTraitMinimumsSatisfied(charm))) {
      return false;
    }
    for (ICharm parentCharm : charm.getLearnPrerequisitesCharms(this)) {
      if (!isLearnable(parentCharm)) {
        return false;
      }
    }
    return true;
  }

  private boolean isExperienced() {
    return context.getBasicCharacterContext().isExperienced();
  }

  private MartialArtsRules getMartialArtsRulesForCharacterType() {
    return getCharmTemplateForCharacterType().getMartialArtsRules();
  }

  private ICharmTemplate getCharmTemplateForCharacterType() {
    return getCharmTemplate(getNativeCharacterType());
  }

  private PrerequisiteModifyingCharms getPrerequisiteModifyingCharms() {
    if (prerequisiteModifyingCharms == null) {
      this.prerequisiteModifyingCharms = new PrerequisiteModifyingCharms(getSpecialCharms());
    }
    return prerequisiteModifyingCharms;
  }

  protected boolean isLearnableWithoutPrerequisites(ICharm charm) {
    if (!isLearnable(charm)) {
      return false;
    }
    for (ICharm parentCharm : charm.getLearnPrerequisitesCharms(this)) {
      if (!isLearned(parentCharm)) {
        return false;
      }
    }
    return true;
  }

  @Override
  public boolean isLearned(String charmId) {
    ICharm charm = getCharmById(charmId);
    return charm != null && isLearned(charm);
  }

  public final boolean isUnlearnable(ICharm charm) {
    ILearningCharmGroup group = getGroup(charm);
    return group.isUnlearnable(charm);
  }

  protected boolean isUnlearnableWithoutConsequences(ICharm charm) {
    ILearningCharmGroup group = getGroup(charm);
    return group.isUnlearnableWithoutConsequences(charm);
  }

  @Override
  public boolean isAlienCharm(ICharm charm) {
    return !isMartialArtsCharm(charm) && isAlienType(charm.getCharacterType());
  }

  private boolean isAlienType(ICharacterType characterType) {
    return characterType != getNativeCharacterType();
  }

  @Override
  public ISpecialCharmConfiguration getSpecialCharmConfiguration(String charmId) {
    ICharm charm = getCharmById(charmId);
    return getSpecialCharmConfiguration(charm);
  }

  @Override
  public final boolean isCompulsiveCharm(ICharm charm) {
    String[] compulsiveCharmIDs = context.getAdditionalRules().getCompulsiveCharmIDs();
    return net.sf.anathema.lib.lang.ArrayUtilities.containsValue(compulsiveCharmIDs, charm.getId());
  }

  @Override
  public final boolean isLearned(ICharm charm) {
    ILearningCharmGroup group = getGroup(charm);
    return group != null && group.isLearned(charm);
  }

  private ILearningCharmGroup getGroupById(ICharacterType characterType, String groupId) {
    List<ILearningCharmGroup> candidateGroups = new ArrayList<ILearningCharmGroup>();
    Collections.addAll(candidateGroups, getCharmGroups(characterType));
    Collections.addAll(candidateGroups, getMartialArtsGroups());
    ICharmTemplate charmTemplate = templatesByType.get(characterType);
    if (charmTemplate.hasUniqueCharms()) {
      IUniqueCharmType uniqueType = charmTemplate.getUniqueCharmType();
      Collections.addAll(candidateGroups, getCharmGroups(uniqueType.getId()));
    }
    for (ILearningCharmGroup group : candidateGroups) {
      if (group.getId().equals(groupId)) {
        return group;
      }
    }
    throw new IllegalArgumentException(
        "No charm group defined for Id: "
            + groupId
            + ","
            + characterType); //$NON-NLS-1$ //$NON-NLS-2$
  }

  @Override
  public final ILearningCharmGroup getGroup(ICharm charm) {
    return getGroupById(charm.getCharacterType(), charm.getGroupId());
  }

  @Override
  public ICharm[] getCharms(ICharmGroup charmGroup) {
    return arbitrator.getCharms(charmGroup);
  }

  @Override
  public List<ICharmFilter> getCharmFilters() {
    return filterSet;
  }
}
public class DefaultExperiencePointConfiguration implements ExperiencePointConfiguration {

  public static final ExperiencePointEntry NO_ENTRY = null;
  private ExperiencePointEntry currentlySelectedEntry = NO_ENTRY;
  private final List<ExperiencePointEntry> entries = new ArrayList<>();
  private final Announcer<ExperiencePointConfigurationListener> control =
      Announcer.to(ExperiencePointConfigurationListener.class);
  private final Announcer<ExperienceSelectionListener> selectionAnnouncer =
      Announcer.to(ExperienceSelectionListener.class);

  @Override
  public ExperiencePointEntry[] getAllEntries() {
    return entries.toArray(new ExperiencePointEntry[entries.size()]);
  }

  @Override
  public ExperiencePointEntry addEntry() {
    ExperiencePointEntry newEntry = addEntryWithoutEvent();
    fireChangeEvent();
    return newEntry;
  }

  private ExperiencePointEntry addEntryWithoutEvent() {
    ExperiencePointEntry newEntry = new DefaultExperiencePointEntry();
    entries.add(newEntry);
    return newEntry;
  }

  @Override
  public void removeEntry() {
    entries.remove(currentlySelectedEntry);
    fireChangeEvent();
    selectForChange(NO_ENTRY);
  }

  private void fireChangeEvent() {
    control.announce().entriesAddedRemovedOrChanged();
  }

  @Override
  public void addExperiencePointConfigurationListener(
      ExperiencePointConfigurationListener listener) {
    control.addListener(listener);
  }

  @Override
  public void addEntrySelectionListener(ExperienceSelectionListener listener) {
    selectionAnnouncer.addListener(listener);
  }

  @Override
  public int getTotalExperiencePoints() {
    int sum = 0;
    for (ExperiencePointEntry entry : getAllEntries()) {
      if (entry.getExperiencePoints() > 0) {
        sum += entry.getExperiencePoints();
      }
    }
    return sum;
  }

  @Override
  public int getExtraSpendings() {
    int sum = 0;
    for (ExperiencePointEntry entry : getAllEntries()) {
      if (entry.getExperiencePoints() < 0) {
        sum -= entry.getExperiencePoints();
      }
    }
    return sum;
  }

  @Override
  public void selectForChange(ExperiencePointEntry entry) {
    this.currentlySelectedEntry = entry;
    selectionAnnouncer.announce().selectionChanged(entry);
  }

  @Override
  public void updateCurrentSelection(String description, int points) {
    currentlySelectedEntry.getTextualDescription().setText(description);
    currentlySelectedEntry.setExperiencePoints(points);
    fireChangeEvent();
  }

  @Override
  public ExperiencePointEntry getCurrentSelection() {
    return currentlySelectedEntry;
  }
}
Beispiel #9
0
public class SpellView implements ISpellView {
  private MagicLearnView magicLearnView;
  private final JPanel content = new JPanel(new GridDialogLayout(1, false));
  private final Announcer<ObjectValueListener> circleControl =
      Announcer.to(ObjectValueListener.class);
  private final SpellViewProperties properties;

  public SpellView(final SpellViewProperties properties) {
    this.properties = properties;
    this.magicLearnView =
        new MagicLearnView() {
          @Override
          protected ListSelectionListener createLearnedListListener(JButton button, JList list) {
            return properties.getRemoveButtonEnabledListener(button, list);
          }
        };
  }

  @Override
  public JComponent getComponent() {
    return content;
  }

  @Override
  public void initGui(Identified[] circles) {
    JComponent selectionPanel = createSelectionPanel(circles);
    IGridDialogLayoutData data = GridDialogLayoutData.FILL_BOTH;
    content.add(selectionPanel, data);
  }

  private JPanel createSelectionPanel(Identified[] circles) {
    JPanel filterBox =
        createFilterBox(
            properties.getCircleLabel(), circles, properties.getCircleSelectionRenderer());
    magicLearnView.init(properties);
    JPanel selectionPanel = new JPanel(new MigLayout(fillWithoutInsets()));
    selectionPanel.add(filterBox, new CC().wrap());
    magicLearnView.addTo(selectionPanel);
    return selectionPanel;
  }

  public JPanel createFilterBox(String label, Object[] objects, ListCellRenderer renderer) {
    JPanel panel = new JPanel(new GridDialogLayout(2, false));
    panel.add(new JLabel(label));
    final JComboBox box = new JComboBox(objects);
    box.setRenderer(renderer);
    panel.add(box, GridDialogLayoutData.FILL_HORIZONTAL);
    box.addActionListener(
        new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent e) {
            circleControl.announce().valueChanged(box.getSelectedItem());
          }
        });
    return panel;
  }

  @Override
  public void addMagicViewListener(IMagicViewListener listener) {
    magicLearnView.addMagicViewListener(listener);
  }

  @Override
  public void addOptionListListener(ListSelectionListener listener) {
    magicLearnView.addOptionListListener(listener);
  }

  @Override
  public void addSelectionListListener(ListSelectionListener listener) {
    magicLearnView.addSelectionListListener(listener);
  }

  @Override
  public void addCircleSelectionListener(ObjectValueListener<CircleType> listener) {
    circleControl.addListener(listener);
  }

  @Override
  public void setLearnedMagic(Object[] spells) {
    magicLearnView.setLearnedMagic(spells);
  }

  @Override
  public void setMagicOptions(Object[] spells) {
    magicLearnView.setMagicOptions(spells);
  }

  @Override
  public void addLearnedMagic(Object[] magics) {
    magicLearnView.addLearnedMagic(magics);
  }

  @Override
  public void addMagicOptions(Identified[] magics, Comparator<Identified> comparator) {
    magicLearnView.addMagicOptions(magics, comparator);
  }

  @Override
  public void removeLearnedMagic(Object[] magics) {
    magicLearnView.removeLearnedMagic(magics);
  }

  @Override
  public void removeMagicOptions(Object[] magics) {
    magicLearnView.removeMagicOptions(magics);
  }

  @Override
  public void clearSelection() {
    magicLearnView.clearSelection();
  }
}
Beispiel #10
0
public class EquipmentModelImpl implements EquipmentOptionsProvider, EquipmentModel {
  private final List<IEquipmentItem> naturalWeaponItems = new ArrayList<>();
  private final Table<IEquipmentItem, IEquipmentStats, List<IEquipmentStatsOption>> optionsTable =
      HashBasedTable.create();
  private final Announcer<ChangeListener> modelChangeControl = Announcer.to(ChangeListener.class);
  private final Announcer<ICollectionListener> equipmentItemControl =
      Announcer.to(ICollectionListener.class);
  private final EquipmentCollection equipmentItems = new EquipmentCollection();
  private IEquipmentTemplateProvider equipmentTemplateProvider;
  private final ChangeListener itemChangePropagator = this::fireModelChanged;
  private CharacterType characterType;
  private MagicalMaterial defaultMaterial;
  private EquipmentHeroEvaluator dataProvider;
  private IArmourStats naturalArmor;

  @Override
  public Identifier getId() {
    return ID;
  }

  @Override
  public void initialize(HeroEnvironment environment, Hero hero) {
    StatsModelFetcher.fetch(hero).addModifierFactory(this);
    Path dataBaseDirectory =
        environment.getDataFileProvider().getDataBaseDirectory(DATABASE_FOLDER);
    EquipmentDirectAccess access = new EquipmentDirectAccess(dataBaseDirectory);
    ObjectFactory objectFactory = environment.getObjectFactory();
    MaterialRules materialRules = new ReflectionMaterialRules(objectFactory);
    CharacterType characterType = hero.getTemplate().getTemplateType().getCharacterType();
    Trait stamina = TraitModelFetcher.fetch(hero).getTrait(AttributeType.Stamina);
    this.naturalArmor = new DefaultNaturalSoak(stamina, characterType);
    EquipmentHeroEvaluatorImpl dataProvider = new EquipmentHeroEvaluatorImpl(hero, materialRules);
    this.characterType = hero.getTemplate().getTemplateType().getCharacterType();
    this.defaultMaterial = evaluateDefaultMaterial(materialRules);
    this.equipmentTemplateProvider = new GsonEquipmentDatabase(access);
    this.dataProvider = dataProvider;
    IEquipmentItem item = createItem(new NaturalWeaponTemplate(), null);
    naturalWeaponItems.add(item);
    new SpecialtiesCollectionImpl(hero)
        .addSpecialtyListChangeListener(new SpecialtyPrintRemover(dataProvider));
    EssencePoolModelFetcher.fetch(hero).addEssencePoolModifier(this);
  }

  @Override
  public void initializeListening(ChangeAnnouncer announcer) {
    addChangeListener(new UnspecifiedChangeListener(announcer));
  }

  @Override
  public EquipmentHeroEvaluator getHeroEvaluator() {
    return dataProvider;
  }

  @Override
  public EquipmentOptionsProvider getOptionProvider() {
    return this;
  }

  @Override
  public IArmourStats getNaturalArmor() {
    return naturalArmor;
  }

  private MagicalMaterial evaluateDefaultMaterial(MaterialRules materialRules) {
    MagicalMaterial defaultMaterial = materialRules.getDefault(characterType);
    if (defaultMaterial == null) {
      return MagicalMaterial.Orichalcum;
    }
    return defaultMaterial;
  }

  @Override
  public IEquipmentItem[] getNaturalWeapons() {
    return naturalWeaponItems.toArray(new IEquipmentItem[naturalWeaponItems.size()]);
  }

  @Override
  public boolean canBeRemoved(IEquipmentItem item) {
    return !naturalWeaponItems.contains(item);
  }

  @Override
  public String[] getAvailableTemplateIds() {
    return equipmentTemplateProvider.getAllAvailableTemplateIds();
  }

  private IEquipmentTemplate loadEquipmentTemplate(String templateId) {
    return equipmentTemplateProvider.loadTemplate(templateId);
  }

  private IEquipmentItem getSpecialManagedItem(String templateId) {
    for (IEquipmentItem item : naturalWeaponItems) {
      if (templateId.equals(item.getTemplateId())) {
        return item;
      }
    }
    return null;
  }

  @Override
  public MagicalMaterial getDefaultMaterial() {
    return defaultMaterial;
  }

  private List<IEquipmentStatsOption> getOptionsList(IEquipmentItem item, IEquipmentStats stats) {
    List<IEquipmentStatsOption> list = optionsTable.get(item, stats);
    if (list == null) {
      list = new ArrayList<>();
      optionsTable.put(item, stats, list);
    }
    return list;
  }

  @Override
  public void enableStatOption(
      IEquipmentItem item, IEquipmentStats stats, IEquipmentStatsOption option) {
    if (item == null || stats == null) {
      return;
    }
    getOptionsList(item, stats).add(option);
    fireModelChanged();
  }

  @Override
  public void disableStatOption(
      IEquipmentItem item, IEquipmentStats stats, IEquipmentStatsOption option) {
    if (item == null || stats == null) {
      return;
    }
    getOptionsList(item, stats).remove(option);
    fireModelChanged();
  }

  @Override
  public boolean isStatOptionEnabled(
      IEquipmentItem item, IEquipmentStats stats, IEquipmentStatsOption option) {
    return item != null && stats != null && getOptionsList(item, stats).contains(option);
  }

  @Override
  public IEquipmentStatsOption[] getEnabledStatOptions(IEquipmentItem item, IEquipmentStats stats) {
    if (item == null || stats == null) {
      return new IEquipmentStatsOption[0];
    }
    List<IEquipmentStatsOption> options = getOptionsList(item, stats);
    return options.toArray(new IEquipmentStatsOption[options.size()]);
  }

  @Override
  public IEquipmentStatsOption[] getEnabledStatOptions(IEquipmentStats stats) {
    if (stats == null) {
      return new IEquipmentStatsOption[0];
    }
    List<IEquipmentItem> itemList = new ArrayList<>();
    itemList.addAll(naturalWeaponItems);
    Collections.addAll(itemList, getEquipmentItems());
    for (IEquipmentItem item : itemList) {
      for (IEquipmentStats stat : item.getStats()) {
        if (stats.equals(stat)) {
          return getEnabledStatOptions(item, stat);
        }
      }
    }
    return new IEquipmentStatsOption[0];
  }

  @Override
  public boolean transferOptions(IEquipmentItem fromItem, IEquipmentItem toItem) {
    boolean transferred = false;
    if (fromItem != null && toItem != null) {
      for (IEquipmentStats fromStats : fromItem.getStats()) {
        List<IEquipmentStatsOption> specialtyList = optionsTable.remove(fromItem, fromStats);
        boolean printCheckboxEnabled = fromItem.isPrintEnabled(fromStats);
        IEquipmentStats toStats = toItem.getStat(fromStats.getId());

        if (toStats != null) {
          transferred = true;
          if (specialtyList != null) {
            optionsTable.put(toItem, toStats, specialtyList);
          }
          toItem.setPrintEnabled(toStats, printCheckboxEnabled);
        }
      }
    }
    return transferred;
  }

  @Override
  public IEquipmentItem[] getEquipmentItems() {
    return equipmentItems.asArray();
  }

  @Override
  public IEquipmentItem addEquipmentObjectFor(String templateId, MagicalMaterial material) {
    IEquipmentTemplate template = loadEquipmentTemplate(templateId);
    if (template == null) {
      return getSpecialManagedItem(templateId);
    }
    return addEquipmentObjectFor(template, material);
  }

  private IEquipmentItem addEquipmentObjectFor(
      IEquipmentTemplate template, MagicalMaterial material) {
    IEquipmentItem item = createItem(template, material);
    equipmentItems.add(item);
    announceItemAndListenForChanges(item);
    return item;
  }

  private IEquipmentItem createItem(IEquipmentTemplate template, MagicalMaterial material) {
    return new EquipmentItem(template, null, null, material, getHeroEvaluator(), equipmentItems);
  }

  @Override
  public void removeItem(IEquipmentItem item) {
    equipmentItems.remove(item);
    announce().itemRemoved(item);
    item.removeChangeListener(itemChangePropagator);
    fireModelChanged();
  }

  @SuppressWarnings("unchecked")
  private ICollectionListener<IEquipmentItem> announce() {
    return equipmentItemControl.announce();
  }

  @Override
  public void updateItem(IEquipmentItem item) {
    fireModelChanged();
  }

  private void fireModelChanged() {
    modelChangeControl.announce().changeOccurred();
  }

  @Override
  public void refreshItems() {
    for (IEquipmentItem item : equipmentItems) {
      if (canBeRemoved(item)) {
        IEquipmentItem refreshedItem = refreshItem(item);
        if (refreshedItem != null) {
          refreshedItem.setPersonalization(item);
          getOptionProvider().transferOptions(item, refreshedItem);
          announceItemAndListenForChanges(refreshedItem);
        }
      }
    }
  }

  private void announceItemAndListenForChanges(IEquipmentItem refreshedItem) {
    announce().itemAdded(refreshedItem);
    refreshedItem.addChangeListener(itemChangePropagator);
    fireModelChanged();
  }

  private IEquipmentItem refreshItem(IEquipmentItem item) {
    String templateId = item.getTemplateId();
    MagicalMaterial material = item.getMaterial();
    removeItem(item);
    return addEquipmentObjectFor(templateId, material);
  }

  @Override
  public void addEquipmentObjectListener(ICollectionListener<IEquipmentItem> listener) {
    equipmentItemControl.addListener(listener);
  }

  @Override
  public MaterialComposition getMaterialComposition(String templateId) {
    IEquipmentTemplate template = loadEquipmentTemplate(templateId);
    return template.getComposition();
  }

  @Override
  public MagicalMaterial getMagicalMaterial(String templateId) {
    IEquipmentTemplate template = loadEquipmentTemplate(templateId);
    return template.getMaterial();
  }

  public void addChangeListener(ChangeListener listener) {
    modelChangeControl.addListener(listener);
  }

  @Override
  public int getMotesExpended() {
    int total = 0;
    for (IEquipmentItem item : equipmentItems) {
      for (IEquipmentStats stats : item.getStats()) {
        if (stats instanceof ArtifactStats
            && item.getAttunementState() == ((ArtifactStats) stats).getAttuneType()) {
          total += ((ArtifactStats) stats).getAttuneCost();
        }
      }
    }
    return total;
  }

  @Override
  public HeroStatsModifiers createStatsModifiers(Hero hero) {
    return CharacterStatsModifiers.extractFromCharacter(hero);
  }

  private class SpecialtyPrintRemover implements ChangeListener {
    private final EquipmentHeroEvaluator dataProvider;

    public SpecialtyPrintRemover(EquipmentHeroEvaluator dataProvider) {
      this.dataProvider = dataProvider;
    }

    @Override
    public void changeOccurred() {
      for (IEquipmentItem item : getEquipmentItems()) {
        for (IEquipmentStats stats : item.getStats()) {
          List<IEquipmentStatsOption> list = optionsTable.get(item, stats);
          if (list == null) {
            return;
          }
          List<IEquipmentStatsOption> optionsList = new ArrayList<>(list);
          for (IEquipmentStatsOption option : optionsList) {
            if (!characterStillHasCorrespondingSpecialty(option)) {
              disableStatOption(item, stats, option);
            }
          }
        }
      }
    }

    private boolean characterStillHasCorrespondingSpecialty(IEquipmentStatsOption option) {
      AbilityType trait = AbilityType.valueOf(option.getType());
      Specialty[] specialties = dataProvider.getSpecialties(trait);
      return ArrayUtils.contains(specialties, option.getUnderlyingTrait());
    }
  }

  @Override
  public String[] getAllAvailableTemplateIds() {
    return equipmentTemplateProvider.getAllAvailableTemplateIds();
  }

  @Override
  public IEquipmentTemplate loadTemplate(String templateId) {
    return equipmentTemplateProvider.loadTemplate(templateId);
  }
}